<?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>Forem: Raphael Otuya</title>
    <description>The latest articles on Forem by Raphael Otuya (@slickdev_raphael).</description>
    <link>https://forem.com/slickdev_raphael</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%2F633921%2F66fcaf57-70a3-4cd3-a925-702df07d40b9.jpeg</url>
      <title>Forem: Raphael Otuya</title>
      <link>https://forem.com/slickdev_raphael</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/slickdev_raphael"/>
    <language>en</language>
    <item>
      <title>USEIMPERATIVE HANDLE HOOK AND NEXT JS CART FUNCTIONALITY
</title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Fri, 11 Feb 2022 17:25:25 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/useimperative-handle-hook-and-next-js-cart-functionality-5624</link>
      <guid>https://forem.com/slickdev_raphael/useimperative-handle-hook-and-next-js-cart-functionality-5624</guid>
      <description>&lt;p&gt;In this tutorial we will be learning about the useImperativeHandle in react, we will be building a simple cart functionality using this hook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up
&lt;/h3&gt;

&lt;p&gt;Create a react project using&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;create react app command&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Create a folder named Component and in that folder create a file “Cart.tsx”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { FC, forwardRef, useImperativeHandle, useState } from "react";

interface ICartProps {
  isOpen: boolean;
  toggleCartOpen: () =&amp;gt; void;
  ref: any;
}

const Cart:FC&amp;lt;ICartProps&amp;gt; = forwardRef((props, cartRef: any) =&amp;gt; {
    const [cart, setCart] = useState([] as any[]);

    useImperativeHandle(cartRef, () =&amp;gt; ({
      addToCart(product: any) {
        let newCart = cart;
        let obj = newCart.find((item: any, i: number) =&amp;gt; {
          if(item.name === product.name){
            newCart[i] = product;
            setCart(newCart);
            return true;
          }
        })
        if(!obj){
          setCart(newCart.concat(product));
          setTotalAmount(newCart.concat(product).reduce(
            (acc: any, item: any) =&amp;gt; acc + item.price * (item.quantity || 1),
            0,
          ));
        }
      },
    }));


    const removeFromCart = (i: number) =&amp;gt; {
      let newCart = cart;
      setCart(newCart.filter((item: any, index: number) =&amp;gt; index !== i));
    };

    const changeProductQuantity = (i: number, quantity: number) =&amp;gt; {
      let newCart = cart;
      newCart[i].quantity = quantity;
      setCart(newCart);

    }

    return (
        &amp;lt;div
          className="flex flex-col justify-between w-20 h-10 fixed bottom-8 right-16"
        &amp;gt;
            &amp;lt;div&amp;gt;
            {cart.map((item: any, i: any) =&amp;gt; (
            &amp;lt;div
                className="flex flex-col w-[95%] py-1 border-b border-solid border-gray-800"
                key={i}
            &amp;gt;
                &amp;lt;div className="flex flex-row justify-between"&amp;gt;
                    &amp;lt;span
                        className="text-black text-base"
                    &amp;gt;
                        {item.name.toUpperCase()}
                    &amp;lt;/span&amp;gt;

                    &amp;lt;button
                        className="py-1 px-2 text-center text-red-600 cursor-pointer"
                        onClick={() =&amp;gt; removeFromCart(i)}
                    &amp;gt;
                        X
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;div className="flex flex-row justify-between"&amp;gt;
                    &amp;lt;p
                        className="text-gray-800 m-0"
                    &amp;gt;    
                        N{item.price} x 
                    &amp;lt;/p&amp;gt;
                    &amp;lt;input
                        type="number"
                        className="w-[20%] py-1 px-2 text-center bg-gray-200 rounded-md"
                        defaultValue={item.quantity || 1}
                        onChange={(e: any) =&amp;gt; onProductQuantityChange(i, e.target.value)}
                    /&amp;gt;

                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            ))}


        &amp;lt;/div&amp;gt;            
        &amp;lt;/div&amp;gt;
    )
});

export default Cart


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

&lt;/div&gt;



&lt;p&gt;In the above file, we first define an interface for our component which declares the props the component expects and their types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; that we are wrapping our component with a forwardRef, this is another react hook that lets us pass a ref from a parent component to a child component, this is necessary to get the useImperativeHandle hook to work.&lt;/p&gt;

&lt;p&gt;We then declare a state to store the cart data.&lt;/p&gt;

&lt;p&gt;The idea with this implementation is, we declare the state that holds the cart data inside the Cart.tsx component and also the functions for adding, removing from the cart, and changing the number of products in the cart. But we want to call the addToCart function from outside this component (eg where the products data will be available), we want to be able to call only this function from outside the component, that’s why we wrap it in the useImperativeHandle function. &lt;br&gt;
The useImperativeHandle takes a ref, which will be passed in from the parent component with the help of the forwardRef handle, and returns an object (eg our addToCart function). &lt;br&gt;
The addToCart function checks if the product is already in the cart, if not, it adds it to the cart state.&lt;/p&gt;

&lt;p&gt;The other functions are used to remove items from the cart and change the no of items in the cart.&lt;/p&gt;

&lt;p&gt;Let us create a ProductComponent file in the Components folder&lt;br&gt;
Paste the following code in the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Image from 'next/image
import { FC } from 'react'
import { HiShoppingCart } from 'react-icons/hi'

interface IProductComponentProps {
    key: any;
    result: any;
    addToCart: (product: any) =&amp;gt; void;
}

const ProductComponent: FC&amp;lt;IProductComponentProps&amp;gt; = (props) =&amp;gt; {

    return (
        &amp;lt;div
            className='flex flex-col bg-white rounded-md w-[70%] md:w-[90%] lg:w-[100%] mx-auto mb-8 
             p-4 lg:flex-row'
        &amp;gt;
            &amp;lt;Image 
                src="https://via.placeholder.com/100"
                alt="product image"
                width={100}
                height={200}
                className='rounded-md justify-center mr-4 h-auto cursor-pointer'
            /&amp;gt;

            &amp;lt;div 
                className="flex flex-col justify-between ml-3 pt-2"
            &amp;gt;
                &amp;lt;h3 
                    className='text-lg font-mono'
                &amp;gt;
                    {props.result.name}
                &amp;lt;/h3&amp;gt;
                &amp;lt;p 
                    className='text-gray-500 text-base line-clamp-2 mb-2'
                &amp;gt;
                    {props.result.description}
                &amp;lt;/p&amp;gt;
                &amp;lt;div 
                    className='flex flex-row justify-between mb-2'
                &amp;gt;
                    &amp;lt;p 
                        className='text-base text-green-700 font-semibold'
                    &amp;gt;
                        {props.result.price }
                    &amp;lt;/p&amp;gt;

                &amp;lt;/div&amp;gt;

                &amp;lt;button
                    className="bg-orange-500 hover:bg-orange-700 text-white font-bold py-2 px-2 w-[50%] 
                    flex justify-center mx-auto rounded-md"
                    onClick={() =&amp;gt; props.addToCart(props.result)}
                &amp;gt;
                    &amp;lt;HiShoppingCart className="text-lg text-center" /&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default ProductComponent


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

&lt;/div&gt;



&lt;p&gt;In the above file, we are declaring the interface for the ProductComponent props, we then use those props to create the component.&lt;/p&gt;

&lt;p&gt;Now, let us create a Results.tsx file in our component folder.&lt;br&gt;
in our Results.tsx file, we will import the Cart.tsx component and the product component, this is where we trigger the useImperativeHandle hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useRef, useState } from "react";
import Cart from "../Components/Cart";

const results = () =&amp;gt; {
    const cartRef = useRef({});

    const addToCart = (newProduct:any) =&amp;gt; {
        cartRef.current?.addToCart(newProduct);
    }

    const [cartOpen, setCartOpen] = useState(false);

 return (
        &amp;lt;div&amp;gt;
        &amp;lt;Cart 
                isOpen={cartOpen}
                toggleCartOpen={openOrCloseCart}
                ref={cartRef}
            /&amp;gt;

      &amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Let’s go over things as it stands right now, in the above file, we are defining a ref named “cartRef”, this is the ref that is passed as props to the cart component in order to trigger the useImperativeHandle in the cart component.&lt;br&gt;
Note that the addToCart function we are defining in this file triggers the addToCart function in the Cart component, which is returned by the useImperativeHandle, by calling the current method on the ref we are passing to the component.&lt;/p&gt;

&lt;p&gt;Let’s pass in some dummy data and display the ProductComponent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useRef, useState } from "react";
import Cart from "../Components/Cart";

const results = () =&amp;gt; {
    const cartRef = useRef({});

    const addToCart = (newProduct:any) =&amp;gt; {
        cartRef.current?.addToCart(newProduct);
    }

    const [cartOpen, setCartOpen] = useState(false);

     const products = [
    {
      name: 'SHIRT',
      description: 'Product Description',
      image: 'https://via.placeholder.com/100',
      price: '$100',
      discount_rate: '10%',
      quantity: 2,
    },
    {
      name: 'Attack on Titan',
      description: 'Manga',
      image: 'https://via.placeholder.com/100',
      price: '$100',
      discount_rate: '10%',
    quantity : 2
    },
    {
      name: 'Demon slayer ',
      description: 'Manga',
      image: 'https://via.placeholder.com/100',
      price: '$100',
      discount_rate: '10%',
      quantity : 1
    },
    ]



 return (
        &amp;lt;div&amp;gt;
        &amp;lt;Cart 
                isOpen={cartOpen}
                toggleCartOpen={openOrCloseCart}
                ref={cartRef}
            /&amp;gt;

        {
          products.map((product:any) =&amp;gt; (
            &amp;lt;ProductComponent 
              key={product.name}
              result={product} 
              addToCart={addToCart}
            /&amp;gt;
          ))
        }
      &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>BELLMAN FORD PATHFINDING ALGORITHM AND DYNAMIC PROGRAMMING</title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Mon, 20 Dec 2021 21:24:36 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/bellman-ford-pathfinding-algorithm-and-dynamic-programming-a4j</link>
      <guid>https://forem.com/slickdev_raphael/bellman-ford-pathfinding-algorithm-and-dynamic-programming-a4j</guid>
      <description>&lt;p&gt;We will look at the single source shortest path problem using Bellman Ford Algorithm, but first let’s understand the concept of Dynamic programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  DYNAMIC PROGRAMMING
&lt;/h2&gt;

&lt;p&gt;Dynamic programming in computer science refers to a method of simplifying and solving problems, by breaking the problem into sub-problems and solving those sub-problems recursively to get the general solution to the original problem. &lt;br&gt;
    There are two attributes a problem should have in order for dynamic programming to solve the problem correctly, they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Optimal substructure: this means that a solution to a given problem can be solved by combining the solutions to its sub-problem, example: you want to get to the 10th floor of a building, you are going to have to find a way to get to the first floor and second floor and third floor, all the way up to the 10th, you get to the 10th floor by climbing the ones below it, assuming you are not a superhero.&lt;/li&gt;
&lt;li&gt;  Overlapping sub-problems: this means that the sub-problem should be the same and can be solved recursively. Ie the sub-problems should be the same so that it can be solved in a repeat manner, take the example above, the sub-problems are the floors of the building, and we solve it by climbing those floors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Steps of Dynamic Programming&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Define sub-problems&lt;/li&gt;
&lt;li&gt;  Guess part of the solution&lt;/li&gt;
&lt;li&gt;  Relate sub-problems to solution&lt;/li&gt;
&lt;li&gt;  Store solutions to sub-problems&lt;/li&gt;
&lt;li&gt;  Solve original problem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s talk about the fourth step &lt;strong&gt;store solutions to sub-problems&lt;/strong&gt;&lt;br&gt;
There are two ways to store the solutions to sub-problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Memoization&lt;/li&gt;
&lt;li&gt;  Build a table ie bottom up approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Memoization&lt;/strong&gt; simply means storing the results of expensive function calls and returning a cached result when the same function call occurs again, in order to optimize and speed up programs. Ie when we first encounter a particular sub-problem, we solve it and store the solution in a data structure, so that when you encounter the same sub-problem, we can return the solution we got from the first sub-problem.&lt;br&gt;
Memoization only works in a Directed Acyclic Graph DAG, because a sub-problem may be called before the solution is stored in memo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bottom up Approach&lt;/strong&gt; means we start solving from the lowest level of sub-problems and accumulate solutions until we reach the top ie Original problem. After we divide our problem into sub-problems, and those sub-problems into smaller ones, we then start solving from the first smallest sub-problem, reusing solutions to sub-problems to solve a larger one.  Because you start solving from the base case ie lowest level of sub-problems, the order in which the problem should be solved must be determined ahead of time.&lt;/p&gt;
&lt;h2&gt;
  
  
  BELLMAN FORD ALGORITHM
&lt;/h2&gt;

&lt;p&gt;Bellman form algorithm follows the principle of Dynamic programming to solve the single source shortest path problem. Richard Bellman in fact, developed the Dynamic programming concept.&lt;br&gt;
Given a graph with “n” number of nodes, Bellman ford algorithm will relax all the edges for “n - 1” times, we are performing relaxation on an edge for “one less than the number of nodes in the graph” times, because we don’t need to calculate the distance of our destination node to another node. We start at our source and relax the edges leading to neighbouring nodes, then we pick those neighbouring nodes and do the same thing for them. After relaxing the edges for “n - 1” times, starting from the source, we follow the path with the smallest edge weight/distance. &lt;br&gt;
Relaxing the edges for “n - 1” times – overlapping sub-problems.&lt;br&gt;
If implemented correctly, the sub-paths of the shortest path are the shortest sub-paths, hence we are following the shortest sub-paths to get the shortest general path to our destination – optimal sub-structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function BellmanFord(list vertices, list edges, vertex source)is

    distance = list of size n
    predecessor = list of size n

    // Step 1: initialize graph
    for each vertex v in vertices do

        distance[v] = infinity  //set distance of all vertices to infinity
        predecessor[v] := null  // And having a null predecessor

    distance[source] := 0       //set distance of source to be 0

    // relax edges repeatedly

    repeat |V|−1 times:
         for each edge (u, v) with weight w in edges do
             if distance[u] + w &amp;lt; distance[v] then
                 distance[v] := distance[u] + w
                 predecessor[v] := u

    // check for negative-weight cycles
    for each edge (u, v) with weight w in edges do
        if distance[u] + w &amp;lt; distance[v] then
            error "Graph contains a negative-weight cycle"

    return distance, predecessor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although Bellman ford has an advantage over Dijkstra with dealing with graphs with negative edges, Bellman ford fails if there is a negative weight cycle in the graph ie total weight of edges in cycle is negative. However, it can indicate if there is a negative weight cycle.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>UNDERSTANDING DIJKSTRA PATHFINDING ALGORITHM </title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Thu, 16 Dec 2021 16:57:41 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/understanding-dijkstra-pathfinding-algorithm-37hi</link>
      <guid>https://forem.com/slickdev_raphael/understanding-dijkstra-pathfinding-algorithm-37hi</guid>
      <description>&lt;p&gt;We will look at using Dijkstra Pathfinding algorithm to solve for single source shortest path problem.&lt;br&gt;
Dijkstra algorithm uses the Greedy Method. Greedy method is said to be an algorithmic paradigm that follows the problem solving approach of making the locally optimal choice at each stage with the hope of finding a global optimum. What this means is that the greedy method solves problems in stages, at each stage, it picks the best input that meets some given criteria, inputs that pass the criteria are considered in the next stage.&lt;br&gt;
Greedy method is used to solve problems that have the following two properties: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Greedy-choice property: a global optimum can be arrived at by selecting a local optimum.&lt;/li&gt;
&lt;li&gt;  Optimal substructure: an optimal solution to the problem contains an optimal solution to its sub problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The procedure for Dijkstra Algorithm should look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Select a vertex&lt;/li&gt;
&lt;li&gt;  Relax the vertex; relaxation means the vertex selected above, we explore its neighbouring vertices ie vertices that can be reached directly from the selected one, if the distance of the selected vertex “d[u]” plus the cost of exploring the neighbouring vertex “C(u, v)”, is less than the distance of the neighbouring vertex, we update the distance of that neighbour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If (d[u] + C(u, v) &amp;lt; d[v])&lt;br&gt;
d[v] = d[u] + C(u, v)&lt;/p&gt;

&lt;p&gt;where u is the selected vertex and v is the neighbouring vertex.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Store explored vertex and the parent vertex (ie the parent vertex means the previous vertex that was relaxed to get the smallest distance) in a data structure&lt;/li&gt;
&lt;li&gt;  Pick neighbour with shortest distance&lt;/li&gt;
&lt;li&gt;  Vertices that cannot be explored directly from the selected one will not be relaxed and should have an initial value of infinity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dijkstra algorithm is exploring each vertex at a time (ie divide the problem into stages) and picking the vertex with the smallest distance (locally optimal).&lt;br&gt;
The sub paths of the shortest paths are the shortest sub paths- Optimal substructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pseudo code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Function Dijkstra(Graph, source)
    For each vertex v in Graph:
        dist[v] = infinity
        previous[v] = undefined
    dist[source] = 0
    A = set of all nodes in Graph
    While A is not empty: 
        u= node in A with smallest dist[]
        remove u from A
        for each neighbour v of u:
            alt= dist[u] + cost(u, v)
            if alt &amp;lt; dist[v]
                dist[v] = alt
                previous[v] = u
    return previous[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Constraints of Dijkstra&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Assumes non-negative edges&lt;/li&gt;
&lt;li&gt;  Follows the current shortest path and may end up exploring more nodes than it should
Dijkstra assumes non negative weights, if any of the edges have a negative value, Dijkstra algorithm may or may not give you the shortest path, this constraint is somewhat tackled in the Bellman ford pathfinding algorithm.
Follows the current shortest path, because Dijkstra has no way of knowing in which direction the destination node is at, it will follow the current shortest path and may end up visiting more nodes than it should, this problem is somewhat tackled in the A search algorithm. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A SEARCH
&lt;/h3&gt;

&lt;p&gt;A search is like an improved version of Dijkstra’s algorithm. It follows similar procedures to Dijkstra but has some additional features. One of the constraints of Dijkstra is that it has no way of knowing what direction the destination is at and so it might visit more than it should. A search algorithm tries to solve this constraint by giving each node a heuristic value which shows the distance of the node from the destination, the closer the node to the destination node, the smaller the heuristic value. Each node in the graph will have the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;G cost&lt;/strong&gt;: this is the cost of edge(s, v) where s is the source and v is the vertex, to put simply, it’s the distance from the node to the starting node.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;H cost&lt;/strong&gt;: the distance from the node to the destination node.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;F value&lt;/strong&gt;: G cost + H cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pseudo code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initialise open and closed list
Make the start vertex current_vertex
Calculate F cost for start vertex
          While current_vertex is not the destination
                 For each vertex adjacent to current_vertex
                          If vertex not in closed list and not in open list then
                               Add vertex to open list
                               Calculate F value
                               If new F cost &amp;lt; existing F cost or there is no existing F value then
            Update F value
    Set parent to be current_vertex
                                 END if
                            END if
                     Next adjacent vertex
                     Add current_vertex to closed list
                     Remove vertex with lowest F value from open list and make current
              END while
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>algorithms</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>CRUD OPERATIONS IN NODE JS WITH EXPRESS AND FIREBASE</title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Sun, 28 Nov 2021 21:04:35 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/crud-operations-in-node-js-with-express-and-firebase-526e</link>
      <guid>https://forem.com/slickdev_raphael/crud-operations-in-node-js-with-express-and-firebase-526e</guid>
      <description>&lt;p&gt;Create, Read, Update and Delete are what is referred to as CRUD.&lt;br&gt;
CRUD operations are present in almost every web app. In this tutorial I will explain how to perform CRUD operations in Node JS and Firebase.&lt;br&gt;
I will assume that you have a project set up and ready.&lt;/p&gt;

&lt;p&gt;CREATE OPERATION:&lt;br&gt;
Let’s create a hypothetical user to demonstrate how to create and save data to the database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/create-user', (req, res) =&amp;gt; {
    const {name, email, password, phoneno, location } = req.body;
    const auth = firebase.auth();
    auth.createUserWithEmailAndPassword(email, password)
        .then((user) =&amp;gt; {
            firebase.firestore().collection("users").doc().set({
                "name": name,
                "email": email,
                "phoneno": phoneno,
                "location": location,
            })
            .then(() =&amp;gt; {
                res.send('User created successfully');
            });
        })
        .catch(err =&amp;gt; {
            res.send(err);
        });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we are getting the user information ie name, email, password and location, from the request body, we then call the firebase auth method and use this auth method to authenticate the user profile using the user’s email and password&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.then((user) =&amp;gt; {
            firebase.firestore().collection("users").doc().set({
                "name": name,
                "email": email,
                "phoneno": phoneno,
                "location": location,
            })
            .then(() =&amp;gt; {
                res.send('User created successfully');
            });
        })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we call an instance of Cloud firestore and save the user data in a document. The “.set()” method overwrites an existing document, if the document does not exist, it will create it with the data provided.&lt;/p&gt;

&lt;p&gt;READ:&lt;br&gt;
We will create a route that logs the user in;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/login', async(req, res) =&amp;gt; {
    try {
        const {email, password} = req.body;
        await firebase.auth().signInWithEmailAndPassword(email, password)
        .then((user) =&amp;gt; {
            firebase.firestore()
                .collection('customers')
                .where('email', '==', email)
                .get()
            .then((users) =&amp;gt; {
                let value = users.docs[0].data();
                res.json(value);
            });
        });
    } catch (err) {
        return res.status(400).send({ message: err.message });
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we call the firebase auth method to authenticate the data provided in the request body, if the data is authenticated successfully, we then go on to find the user document in our cloud firestore using the user email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            .then((users) =&amp;gt; {
                let value = users.docs[0].data();
                res.json(value);
            });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then call the docs method on the result returned from the firebase query to get the result as a list and the pick the first document (should only contain one document) and return it.&lt;/p&gt;

&lt;p&gt;FIND ONE DOCUMENT&lt;br&gt;
Here we are going to query the cloud firestore collection “users” for one document using the email provided and return the first document&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get('/find-user', async(req, res) =&amp;gt; {
    const {email} = req.body;
    await firebase.firestore()
        .collection('users')
        .where('email', '==', email)
        .get()
    .then((users) =&amp;gt; {
        let value = users.docs[0].data();
        res.send(value);
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FIRLTER DOCUMENTS BY MORE THAN ONE FIELD&lt;br&gt;
We are going to query our cloud firestore and filter the data by more than one field in the document. Assume we want to find users in a particular location, that are also verified and also currently online.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/find-user', async (req, res) =&amp;gt; {
    let location = req.body.location;
    let query = await firebase.firestore()
        .collection('users')
        .where('location', '==', location);

        if(query != "") {
            query = await query.where('verified', '==', "true");
        }
        if(query != "") {
            query.where('status', '==', 'online')
            .get()
            .then(snapshots =&amp;gt; {
                if(snapshots.empty) {
                return null;
                }
                let results = snapshots.docs.map(doc =&amp;gt; doc.data());
                return res.json(results[0]);
            });
        }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The “.where” method returns a collection query which we first check to see if it is not empty, if it’s not, we then filter by other fields, then we loop through the results and return the data of the first document.&lt;/p&gt;

&lt;p&gt;UPDATE:&lt;br&gt;
We will use the “.update” method to update an existing document in the cloud firestore. It only works if the document already exists before calling the update method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/update-user', async(req, res) =&amp;gt; {
    const {name, email, phoneno, location } = req.body;
    try{
        if(!req.body){
            return res
                .status(400)
                .send({ message : "Data to update can not be empty"});
        }
        await firebase.firestore().collection('users')
        .where('email', "==", email)
        .update({
            name : name,
            description : req.body.description,
            phoneno : phoneno,
            location : location,
        }).then((ref) =&amp;gt; {
            res.json(ref.data());
        });
    }
    catch(err){res.status(500).send({ message : err.message || "Error Occurred while updating" });
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DELETE: &lt;/p&gt;

&lt;p&gt;Delete operation is pretty straightforward, call the “.delete” method on the document you want to remove&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/delete-user', async(req, res) =&amp;gt; {
    const {email} = req.body;
    await firebase.firestore()
    .collection('users')
    .where('email', "==", email)
    .delete()
    .then((ref) =&amp;gt; {
        res.json(ref.data());
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DELETE VALUE FROM ARRAY:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/delete-value', async(req, res) =&amp;gt; {
    const {email, value} = req.body;
    try{
        await firebase.firestore().collection('users').doc(email).update({
            [value] : firebase.firestore.FieldValue.delete()
        });
        res.json('successful operation');
    }
    catch(err){res.status(500).send({ message : err.message || "Error Occurred while deleting value" });
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>firebase</category>
      <category>node</category>
    </item>
    <item>
      <title> ROLE BASED ACCESS CONTROL IN ADONIS JS WITH NPM ROLE-ACL </title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Wed, 17 Nov 2021 16:14:00 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/role-based-access-control-in-adonis-js-with-npm-role-acl-4jg3</link>
      <guid>https://forem.com/slickdev_raphael/role-based-access-control-in-adonis-js-with-npm-role-acl-4jg3</guid>
      <description>&lt;p&gt;Role based access control or RBAC as it would be called throughout the remainder of this article, refers to an authorization process based on user defined roles in the organization, example: a team-member can create and update collections but they can’t delete a collection, only the team-admin role has authority to delete collections. &lt;br&gt;
In this article we will be creating an API that implements the above example along with only allowing team-admins and team-members access to collections belonging to their teams and no other teams’ collections. &lt;/p&gt;

&lt;p&gt;We will be using Adonis JS which is a Node JS framework along with the Role-acl package.&lt;/p&gt;

&lt;p&gt;I will assume you have an Adonis server, with the Lucid ORM and a Database already set up.&lt;br&gt;
For authentication we will be taking off from the where this last tutorial, &lt;a href="https://dev.to/slickdev_raphael/social-login-and-authentication-in-adonis-js-93a"&gt;social authentication in Adonis JS&lt;/a&gt;, we talked about using Ally package for social Authentication using google. &lt;/p&gt;

&lt;p&gt;Let’s create the User, teams and collections models and migrations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:model User -m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:model Team -m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:model collection -m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the user model file, we will add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DateTime } from 'luxon'
import { column, BaseModel } from '@ioc:Adonis/Lucid/Orm'

export default class Users extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public name: string;

  @column()
  public avatar_url: string | null;

  @column({isPrimary: true})
  public email: string;

  @column()   
  public role: string;

  @column()   
  public providerId: string;

  @column()
  public provider: string;

  @column()
  public teams: {} | null;

  @column()
  public rememberMeToken?: string

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime


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

&lt;/div&gt;



&lt;p&gt;Then the user migrations file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class UsersSchema extends BaseSchema {
  protected tableName = 'users'

  public async up() {
    this.schema.createTable(this.tableName, (table) =&amp;gt; {
      table.increments('id').primary()
      table.string('name').notNullable();
      table.string('avatar_url');
      table.string('email').notNullable().unique();
      table.string('role').defaultTo('basic');
      table.string('provider');
      table.string('provider_id');
      table.string('remember_me_token');

      table.json('teams');
      /**
       * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true }).notNullable()
      table.timestamp('updated_at', { useTz: true }).notNullable()
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}


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

&lt;/div&gt;



&lt;p&gt;The teams model and migrations will look like this:&lt;br&gt;
Team model :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class Team extends BaseModel {
  @column()
  public id: number

  @column({ isPrimary: true })
  public uid: string 

  @column()
  public name: string

  @column()
  public owner_email: string[]

  @column()
  public members: string[]

  @column()
  public collections: string[]

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import BaseSchema from '@ioc:Adonis/Lucid/Schema'
import { generateRandomKey } from '../../Utils/generateRandomKey'

export default class Teams extends BaseSchema {
  protected tableName = 'teams'

  public async up () {
    this.schema.createTable(this.tableName, (table) =&amp;gt; {
      table.increments('id')
      table.string('uid').defaultTo( generateRandomKey())
      table.string('name').notNullable()
      table.specificType('owner_email', 'text[]').notNullable()
      table.specificType('members', 'text[]').defaultTo('{}')
      table.specificType('collections', 'text[]').defaultTo('{}')

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}

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

&lt;/div&gt;



&lt;p&gt;The collections’ model and migration file;&lt;br&gt;
Collections model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class Collection extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public collectionId: string

  @column()
  public name: string

  @column()
  public collectionOwnerId: string

  @column()
  public description: string | null

  @column()
  public team: string

  @column()
  public resultsAddress: string

  @column.dateTime()
  public executionTime: DateTime | null

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import BaseSchema from '@ioc:Adonis/Lucid/Schema'
import { generateRandomKey } from '../../Utils/generateRandomKey'

export default class Collections extends BaseSchema {
  protected tableName = 'collections'

  public async up () {
    this.schema.createTable(this.tableName, (table) =&amp;gt; {
      table.increments('id')
      table.string('collection_id').defaultTo(generateRandomKey())
      table.string('name').notNullable().unique()
      table.string('collection_owner_id').notNullable()
      table.string('description', 255).nullable()
      table.string('team_id').notNullable()
      table.string('results_address').notNullable()
      table.timestamp('execution_time',  { useTz: true }).notNullable()

      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}

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

&lt;/div&gt;



&lt;p&gt;We will then install the &lt;a href="https://www.npmjs.com/package/role-acl"&gt;Role-acl package&lt;/a&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i role-acl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will create a middleware that checks every request to a protected route, it checks if the user&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Is a part of the team?&lt;/li&gt;
&lt;li&gt;  Is the team-admin?&lt;/li&gt;
&lt;li&gt;  Is a team member
We will also define the team-admin and team member roles in this middleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The team middleware file will be 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;import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Database from '@ioc:Adonis/Lucid/Database';
import { AccessControl } from 'role-acl'

let ac = new AccessControl();

ac.grant('basic')
    .execute('create').on('team')

    .grant('team-member')
        .extend('basic')
        .execute('post').on('collections')
        .execute('get').on('collections')
        .execute('put').on('collections')

    .grant('team-owner')
        .extend('team-member')
        .execute('delete').on('collections')

export default class TeamCollectionsMiddleware {
  public async handle ({auth, request, params}: HttpContextContract, next: () =&amp;gt; Promise&amp;lt;void&amp;gt;) {
    // code for middleware goes here. ABOVE THE NEXT CALL
    let userPermission!: string;
    const userEmail: string = auth.user.email
    //CHECK IF USER IS TEAM OWNER
    let user = await Database.from('teams')
      .where((query) =&amp;gt; {
        query
        .where('uid', params.id)
        .where("owner_email", '@&amp;gt;', [userEmail])
        userPermission = 'team-owner'
      })

       //CHECK IF USER IS TEAM MEMBER
      if(user.length === 0){
        user = await Database.from('teams')
        .where((query) =&amp;gt; {
          query
            .where('uid', params.id)
            .where("members", '@&amp;gt;', [userEmail])
            userPermission = 'team-member'
        })
      }

      if  (user.length == 0) {
        throw new Error("You are not a member of this team")
      }

      const permission = await ac.can(userPermission).execute(request.method()).on('collections'); 
      if(permission.granted) await next();
      else throw new Error('You are not allowed to perform this action');
  }
}


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

&lt;/div&gt;



&lt;p&gt;Here we defined the basic role, team-owner, team-member and owner role. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Basic role: has the permission to create teams&lt;/li&gt;
&lt;li&gt;  Team-member: can create a collection ie “post”, read and update a collection ie “get and put”.&lt;/li&gt;
&lt;li&gt;  Team-owner: can do everything the team-member role has permission to do and can also delete collections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the body of the middleware, we created a variable to store the user permission status and also another variable to get the user email from the auth session data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let user = await Database.from('teams')
      .where((query) =&amp;gt; {
        query
        .where('uid', params.id)
        .where("owner_email", '@&amp;gt;', [userEmail])
        userPermission = 'team-owner'
      })


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

&lt;/div&gt;



&lt;p&gt;In the above code snippet, we are checking the teams table in the database, we then get the team through the params (teams id will be passed in with the route), then we check if the owner column contains the user email, if it does we set the userPermission variable to be “team-owner”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       //CHECK IF USER IS TEAM MEMBER
      if(user.length === 0){
        user = await Database.from('teams')
        .where((query) =&amp;gt; {
          query
            .where('uid', params.id)
            .where("members", '@&amp;gt;', [userEmail])
            userPermission = 'team-member'
        })
      }

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

&lt;/div&gt;



&lt;p&gt;Else, if the owner_email column does not contain the user email, we then check the members’ column, if it contains the user email, if it does we update the userPermission to be “team-member”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if  (user.length == 0) {
        throw new Error("You are not a member of this team")
      }


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

&lt;/div&gt;



&lt;p&gt;If user email is not in the members’ column or owner column, then the user is not part of the team and we throw an error.&lt;/p&gt;

&lt;p&gt;We then check the userPermission variable to see if the user has the right permission to perform the request they want to perform, if they do then the request is sent to the controller, if they do not, an error will be thrown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      const permission = await ac.can(userPermission).execute(request.method()).on('collections'); 
      if(permission.granted) await next();
      else throw new Error('You are not allowed to perform this action');

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

&lt;/div&gt;



&lt;p&gt;We will now define the collections controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node ace make:controller Collection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following code in the controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema } from '@ioc:Adonis/Core/Validator'
import Collection from 'App/Models/Collection'

export default class CollectionsController {
    public async createCollection ({ request, response }: HttpContextContract) {
        const data = await schema.create({
            name: schema.string({ trim: true }),
            description: schema.string({ trim: true }),
            collectionOwnerId: schema.string({ trim: true }),
            resultsAddress: schema.string({ trim: true }),
            executionTime: schema.date(),
        });

        const validatedData = await request.validate({schema: data});

        const newCollection = await Collection.create(validatedData);

        return response.status(201).json(newCollection);
    }

      public async getCollection ({ params, response }: HttpContextContract) {
        const collection = await Collection.findByOrFail('collection_id', params.id);

        return response.status(201).json(collection);
    }

    public async getAllCollectionsForATeam ({params, response }: HttpContextContract) {
        const collections = await Collection
            .query()
            .where('team_id', params.teamId)

        return response.status(201).json(collections);
    }

       public async updateCollection ({ params, request, response }: HttpContextContract) {
        const collection = await Collection.findByOrFail('collection_id', params.id);

        const data = await schema.create({
            name: schema.string({ trim: true }),
            description: schema.string({ trim: true }),
            collectionOwnerId: schema.string({ trim: true }),
            resultsAddress: schema.string({ trim: true }),
            executionTime: schema.date(),
        });

        const validatedData = await request.validate({schema: data});

        await collection.merge(validatedData);

        await collection.save();

        return response.status(204).json(collection);

    }

    public async deleteCollection ({ params, response }: HttpContextContract) {
        const collection = await Collection.findByOrFail('collection_id', params.id);

        await collection.delete();

        return response.status(204);
    }

}


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

&lt;/div&gt;



&lt;p&gt;We will then add the middleware to the routes for the collections&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//COLLECTIONS ROUTES
Route.group(() =&amp;gt; {
  Route.get('/get-collections', 'CollectionsController.getAllCollectionsForATeam'); // GET ALL COLLECTIONS FOR A TEAM
  Route.get('/get-collections/:id', 'CollectionsController.getCollection'); // GET ONE COLLECTION
  Route.post('/create-collections', 'CollectionsController.createCollection'); // CREATE COLLECTION
  Route.put('/collections/update/:id', 'CollectionsController.updateCollection'); // UPDATE COLLECTION
  Route.delete('/collections/delete/:id', 'CollectionsController.deleteCollection'); // DELETE COLLECTION
})
.middleware(['auth', 'teamCollectionMiddleware']);

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

&lt;/div&gt;



&lt;p&gt;That’s it. Tell me what you think in the comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>node</category>
      <category>authorization</category>
    </item>
    <item>
      <title>Social login and authentication in Adonis JS </title>
      <dc:creator>Raphael Otuya</dc:creator>
      <pubDate>Wed, 03 Nov 2021 19:54:00 +0000</pubDate>
      <link>https://forem.com/slickdev_raphael/social-login-and-authentication-in-adonis-js-93a</link>
      <guid>https://forem.com/slickdev_raphael/social-login-and-authentication-in-adonis-js-93a</guid>
      <description>&lt;p&gt;In this tutorial we will be going through user authentication in Node JS specifically Adonis JS.&lt;br&gt;
We will be using social login methods example: sign in with Facebook, Google and GitHub, using an Adonis JS package called Ally. &lt;/p&gt;

&lt;p&gt;Let’s get into it.&lt;/p&gt;

&lt;p&gt;I will assume you have an Adonis project already set up with lucid or your preferred method for storing information, we will then need to install the following packages:&lt;br&gt;
• Ally&lt;br&gt;
• Auth&lt;br&gt;
Ally is a social login tool for Adonis JS, it has to be installed and configured separately. &lt;/p&gt;

&lt;p&gt;Run the following commands to install and configure Ally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @adonisjs/ally

node ace configure @adonisjs/ally.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have to update your “clientId”, “clientSecret” and “callbackUrl” in the configuration file stored in the config/ally.ts directory. &lt;br&gt;
ClientId and clientSecret are gotten from which platform you choose to use ie facebook, Google, Github, while the callbackUrl is the url which you will define to handle the response gotten from the provider.&lt;br&gt;
For this tutorial, i will be using the google provider.&lt;/p&gt;

&lt;p&gt;Step 1: make user model and migration.&lt;br&gt;
Using the cli command:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node ace make:model User –m&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The “-m” flag creates a migration along with the model. &lt;br&gt;
Add other fields you want to store on the table.&lt;/p&gt;

&lt;p&gt;The user migration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class Users extends BaseSchema {
  protected tableName = 'users'

  public async up () {
    this.schema.createTable(this.tableName, (table) =&amp;gt; {
      table.increments('id');
      table.string('name').notNullable();
      table.string('avatar_url');
      table.string('email').notNullable();
      table.string('provider');
      table.string('provider_id');


      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty self-explanatory, we are creating a table with column: &lt;br&gt;
• Id: auto incrementing counter for the rows&lt;br&gt;
• Name: the name of the user which we will get from the provider &lt;br&gt;
• Avatar_url: user profile picture url, stored as a string&lt;br&gt;
• Email: user email&lt;br&gt;
• Provider: the driver the user used to sign up for our app&lt;br&gt;
• Provider id: a unique id gotten from the provider&lt;br&gt;
The created_at and updated_at are auto generated and will be updated automatically on creation and update of rows ie user.&lt;br&gt;
The user model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

export default class User extends BaseModel {
  @column()
  public id: number

  @column()
  public name: string;

  @column()
  public avatar_url: string;

  @column({isPrimary: true})
  public email: string;

  @column()   
  public providerId: string;

  @column()
  public provider: string;

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime
}

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

&lt;/div&gt;



&lt;p&gt;Note that the content of your model should always match your migration.&lt;/p&gt;

&lt;p&gt;Step 2: Create signup controller&lt;br&gt;
Use the cli command: node ace make: controller GoogleSignup&lt;br&gt;
A file will be created in the app/controllers directory. Paste the following code in the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User';

export default class GoogleSignInsController {
    public async redirect({ally}: HttpContextContract) {
        return ally.use('google').redirect() 
    }

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

&lt;/div&gt;



&lt;p&gt;We are creating a method that redirects the user to the OAuth providers website for authentication.&lt;/p&gt;

&lt;p&gt;Step 3: Handle callback&lt;br&gt;
Paste the following code in the same file, it includes the method created above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User';

export default class GoogleSignInsController {
    public async redirect({ally}: HttpContextContract) {
        return ally.use('google').redirect() 
    }

    public async handleCallback ({ally, auth, response}: HttpContextContract) {
        const googleUser = ally.use('google');

        /**
         * User has explicitly denied the login request
         */
        if (googleUser.accessDenied()) {
            return 'Access was denied'
        }

        /**
         * Unable to verify the CSRF state
         */
        if (googleUser.stateMisMatch()) {
            return 'Request expired. try again'
        }

        /**
         * There was an unknown error during the redirect
         */
        if (googleUser.hasError()) {
            return googleUser.getError()
        }

        /**
         * Finally, access the user
         */
        const user = await googleUser.user();

        const findUser = {
            email: user.email as string
        }

        const userDetails = {
            name: user.name as string,
            email: user.email as string,
            avatar_url: user.avatarUrl as string,
            provider_id: user.id as string,
            provider: 'google'
        } 

        const newUser =await User.firstOrCreate(findUser, userDetails);

        await auth.use('web').login(newUser)
        response.status(200);

        return newUser;
    }

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

&lt;/div&gt;



&lt;p&gt;Here we are handling all the use cases if the sign in failed before we store the user in the database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const user = await googleUser.user(); //stores the user information object gotten back from google
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const newUser =await User.firstOrCreate(findUser, userDetails);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we first query the database using the email of the user which is stored in the findUser object, if the email exists in the database, return the first one, otherwise create a new user with the userDetails object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await auth.use('web').login(newUser)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we use the built in adonis auth package to log the user in and create a session.&lt;/p&gt;

&lt;p&gt;Step 3: Attach controllers to route&lt;br&gt;
In the routes file, we will create a route for calling and initializing the OAuth provider and another route for handling the callback&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SIGN IN ROUTES
Route.get('/google-signin', 'GoogleSignInsController.redirect');

//OAuth CALLBACK
Route.get('/google-signin-callback', 'GoogleSignInsController.handleCallback');

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

&lt;/div&gt;



&lt;p&gt;Note that the route above will be the route you input as your callbackUrl in the ally configuration file.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>node</category>
    </item>
  </channel>
</rss>
