AdminBro – Part one

This blog is the getting started guide for AdminBro, react based admin panel for node js application. In this blog, we are going to create a demo app where an admin can manipulate users’ and companies’ data using an admin panel created by AdminBro. I am going to divide this tutorial into different parts because of its length. The code for each part will be pushed to a separate branch on Github[https://github.com/zelalem-12/adminbro-tutorial].

Note: This blog is for those who are already familiar with node js with express framework, mongoose and react.

For the last couple of years, I have been working mostly on Node projects. Since then, I have been looking for a role-based admin panel for my node js applications. Recently, I came across AdminBro, which uses your existing framework to render its routes. It is really useful because it not only allowed me to add an admin panel in a very short time frame but also provided various customizing options. More importantly, AdminBro allows you to manage your database resources. It supports Mongoose(MongoDB ORM) and Sequelize for your Relational Database ORMs and It works flawlessly with Express.js and Hapi.js. Not just that, AdminBro can be integrated with any Node.js Framework. As defined on AdminBro home page,

AdminBro is An automatic admin interface that can be plugged into your application. You, as a developer, provide database models (like posts, comments, stores, products, or whatever else your application uses), and AdminBro generates UI which allows you (or other trusted users) to manage content.

Why should you use it:

  1. Supports both relational(Sequalize) and non relational(mongoose) Database Models
  2. Built on the modern stack: Node.js, React.js, Styled-components
  3. Supports heavy customization
  4. Its awesome look

……………………………………………………………………………

In this part the blog, we will be able to

  1. Create app and models using express and mongoose
  2. Install and integrate AdminBro

In the next part, we will be doing more customization to our admin panel.

Create app and models

Before starting with AdminBro you must have an Express.js application with your models defined. So, in the first section, we are going to set up a Node.js Server with Express.js and also define our models in mongoose.

Create a simple express application

const express = require(‘express’)
const bodyParser = require(‘body-parser’)

const app = express()
app.use(bodyParser.json())

app.get(‘/’, (req, res) => {
res.send(‘Hello World’)
})

//…
// Your Routes
//…

const PORT = 3000
app.listen(PORT, () => {
console.log(? Server started on PORT: ${PORT})
})

Create your database model To keep things simple we are just building a User Model in mongoose

const mongoose = require(‘mongoose’)

const userSchema = mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
avatar_url: { type: String },
bio: { type: String }
})

module.exports = User = mongoose.model(‘User’, userSchema)

Add your models in your app and Connect to MongoDB

Connect to your MongoDB database in your app.js file. To do this add the following code in your app.js

Import MongoDB

const mongoose = require(‘mongoose’)

// Connect to MongoDB
mongoose
.connect(YOUR_MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log(‘? MongoDB Connected…’)
.catch(err => console.log(err))

Install and integrate AdminBro

Install the AdminBro and necessary adapter(s)

npm i admin-bro admin-bro-expressjs admin-bro-mongoose express-formidable

Create an admin.js file to Keep your AdminBro Configurations

const AdminBro = require(‘admin-bro’)
const AdminBroExpress = require(‘admin-bro-expressjs’)
const AdminBroMongoose = require(‘admin-bro-mongoose’)

const User = require(‘./model’)

AdminBro.registerAdapter(AdminBroMongoose)
const adminBro = new AdminBro({
rootPath: ‘/admin’
resources: [
{
resource: User,
options: {
// check the documentation for options you can use
}
}
],
})

module.exports = adminRouter = AdminBroExpress.buildRouter(adminBro)

Add the AdminBro Router

Add the AdminBro Router in your app.js

app.use(‘/admin’, require(‘./admin’))

Start the server and test

Now start your server and go to https://localhost:3000/admin or to your local server’s URL(If you’re using different configuration).

You can see the AdminBro dashboard with your Models on the Left Sidebar. You can now perform all operations on your data with the AdminBro Panel.

In this part, we are done with the introduction, setup, and integration. In the next part, we will add customizations that unlock best the features of AdminBro.

Documentation: https://softwarebrothers.github.io/admin-bro-dev/

GitHub: https://github.com/SoftwareBrothers/admin-bro

NPM: https://www.npmjs.com/package/admin-bro

Always Try to use Memoized Components in React

Because of the fact that whenever the states and props of react components or their parent components change the component and its child component will re-render, it will create a huge performance problem or might lead to infinite loop if not watched carefully and implemented with such appropriate caution.

Memoizing in React is a performance feature of the framework that aims to speed up the render process of components. The technique is used in a wide spectrum of disciplines, from game engines to web applications.

This article explores memoization techniques within React.


Memoizing is a well known concept in computer programming, aiming to speed up programs by caching results of expensive function calls and re-using those cached results to avoid repeating those expensive operations. While Memoizing often-times saves processing cycles, there is a limit to how much it can be used — this is of course dictated by a system’s memory limits.            

                
When it comes to React, we are caching the result of a component’s render() method — or simply the returned JSX of a functional component.


It is wise to consider how much of your application will be cached via memoizing Since mobile devices will inevitably have less memory to utilize than laptop or desktop counterparts.

here are two lists of ways that you can introduce memoization in react

1. Memoization with useMemo() and useCallback() hooks

Memorize hooks need to remember data or functions that:

  1. Might cause re-render when re-rendering is not needed
  2. Preserve or cache data or function so new copies are not created.
  • useMemo() returns a memoized value, 
useMemo(()=>{},[deps])
  • whereas useCallback returns a memoized callback
useCallback(()=>{},[deps])


Both React.useMemo and React.useCallback receive a function as its first argument and a dependencies array as the second one. The hook will return a new value only when one of the dependencies value changes.


The main difference is that React.useMemo will call a function and return its result while React.useCallback will return a function without calling it. The Memoized hooks caches the computationally expensive functions data and return them a given one of the dependency array is changed. 

2. Memoization with react Reselects


Well most of the time we often use reselectors in react state management tool with selectos. But before going to reselectors we have to understand what selectos are.


At their most simple, Selectors are simply functions that are used to select a subset data from a larger data collection. Basic selectors (i.e simple functions that return a subset of data) are beneficial because they encapsulate knowledge of where to find that particular subset of data, they are also reusable and flexible


Selectors:


1) Have knowledge of the whereabouts or path to find a particular subset of data and
2) Return with the requested subset of data


Why Use Reselect to Create Selectors?


The short answer is: for performance as Reselect provides a wrapper for creating selectors that are memoized.


The definitions of selectors that you’ve seen so far have only been responsible for retrieving data as it appears in the redux store. This would only likely be enough in small apps where the data that app needs can be found directly in the store. However, in larger and more complex apps, it’s advisable to keep the store with minimal amount of information as it cuts down on repetition and generally helps to avoid highly nested data that results in more complex reducer logic. This is especially relevant for apps that are structured with a normalized store. The biggest benefit of Reselect is that selectors created with the library are memoized and therefore will only re-run if their arguments change.


here is an example of selectors obtaining a specific data from redux state:                        
here a selector gets the belt data from shop store,


//selector.js
// Note: selectors are usually created in a relevant reducer file or a separate selectors file
const getBelts = (state) => state.shop.items.belts;
class DisplayBelts extends React.Component {
render() {
return this.props.belts.map(belt => );
}
}
// and specify the result of selectors data to props
const mapStateToProps = state => {
return {
belts: getBelts(state)
}
}


By doing this we encapsulate knowledge of where to find that particular subset of data, they are also reusable and flexible. But if we ask the selector to give us a data that is not different from the previous one the selector would have to go again and get the data for which it doesnt have to do, because it can easily cache the data in a memory and read  that cached data from memory the next time it is asked the get that data. This way it would a huge amount of computation, time and keeps the react  component from rerendering. This is a place to use Reselect


Reselect offers up a function called createSelector() that’s used to create memoized selectors

import { createSelector } from 'reselect'
const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc + item.value, 0)
)
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
export const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax })
)
To retrieve state in a component
import {totalSelector} from '/selector'
import {createStructuredSelector} from 'reselect'
class Inventory extends React.Component {
render() {
return The shop's total inventory is: ${this.props.inventoryValue}
}
}
const mapStateToProps = createStructuredSelector({
inventoryValue: totalSelector()
})


Memoizing Considerations
  • To use these APIs effectively, consider the following points to ensure your                        
    apps remain bug free while leveraging the performance gains of Memoizing:
  • Side effects (anything that affects something outside the scope of the                      
    function being executed) should not be performed in a memoized function.                    
    Leave this to the commit phase of the component, after its render phase has                          
    completed execution
  • Memoizing is performed in the render phase of a component, and side                        
    effects should typically be used in componentDidMount,            
    componentDidUpdate, and componentDidCatch with class components, or            
    useEffect with functional components
  • Use the Profiler to record the performance of your app before and after                          
    Memoizing. If the performance gains are not considerable, it may be worth                      
    removing the added complexity Memoization brings in certain areas of your                    
    app where there is not a huge performance gain, freeing up more memory and                         
    decreasing boilerplate
  • Compartmentalise a component into child components to leverage                
    memoization if you need to, such as in forms whereby static components such                       
    as buttons, labels and icons may be combined with dynamic components that                      
    need re-rendering, such as with validation. Separate these and memoize                  
    where possible — this is especially useful with complex SVG icons that contain                        
    a lot of markup

Web development in Ethiopia using Kubernetes

Why is it important we discuss Kubernetes in Ethiopia.

The main struggle in Web development in Ethiopia is a proper server infrastructure. If you have been at any governmental agencies to get any kind of work approved you probably have stayed for hours waiting for a system to get back online. One of the main reason for this kind of issue is the servers running one version of application. Whenever a bug or uncaught error is found in the server the entire city waits for the servers to restart. This phenomenon is also known as system shortage(system yelem). By creating a stable infrastructure we can minimize the number of hour spent waiting in line.

What is Kubernetes ?

The name Kubernetes originated from Greek meaning pilot, This software released by google as an open-source software in 2014 allows one to freely manage containerized applications and services.

A simple way to view it is to compare it to a symphony orchestra. Where there is one conductor instructing in who, how high and when each musician and instrument should play.  Each instrument and musician represents a containerized application or service.
The conductor also called the master node will instruct the musicians in kubernetes terminology called pods, services, ingress to work together in a perfect harmony. 

Kubernetes makes it easy to manage hardware resources, scale applications and manage release. With almost no downtime it is a good way to roll out a new version without system shortage. It also solves the Hardware shortage by only allocating resources to the application that is currently in need of it. No more system shortage  and smoother web development in Ethiopia.
Without going deep into how it works we will discuss the main idea of kubernetes and why we use it in our development process in Addis software.




Recommend to installing minikube for testing kubernetes on local development.

Kubernetes cluster
A cluster is network of virtual or physical machines known as nodes. Nodes are designated hardware space to run the kubernetes application and all the applications running on each node. Each application runs wrapped within temporary VMs called pods. A promotion assigned to one of the nodes to be a master node. A kubernetes cluster contains a masted node, at least one worker node, pods running inside each node

Master node

Main task is to  automatically handles scheduling the pods across the Nodes in the cluster from a given rule    

kubectl describe node <insert-node-name-here>

Deployment

Is Kubernetes api to manage a deployment  and allows you to describe an application’s life cycle, such as which images to use for the app. An example of a simple configuration file. 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-app
  template:
    metadata:
      labels:
        run: my-app
    spec:
      containers:
      - name: hello-app
        image: gcr.io/google-samples/hello-app:1.0



To run the above deployment save the above snippet in a yaml file and run kubectl apply -f deploymentFileName , now you have created a deployment

To check the status of the deployment run kubectl describe deployment [DEPLOYMENT_NAME]. If you have forgotten your deployment name then you can run  kubectl get deployments to view a list of deployments. When a deployment is done it will create a pod running your application. When it’s time for you to scale up your application you can simply run the following command with the number of replicas you want to create kubectl scale deployment [DEPLOYMENT_NAME] –replicas [NUMBER_OF_REPLICAS] and  to remove a deployment kubectl delete deployment [DEPLOYMENT_NAME]

Pods

A pod is an isolated space where a containerized application runs in, it is viewed as a minimal running unit. it’s also mostly viewed as a temporary unit rather than a permanent one. It is also considered as the final resting place for your application code. Each pod is assigned an id, internal and external ip, and the containers will share the pods networks space. 

Once a deployment is done, you should be able to see the pod list by running kubectl get pods.

  • Pending: a pod have been accepted by kubernetes master and is in progress
  • Running: a deployment successfully started a pod
  • Succeeded: a pod is successfully terminated and will not be started
  • Failed: a container within the pod have some kind of error and could not be started
  • Unknown: the status of the pod is unknown

Service 

Service is kubernetes api to manage a running containers network, as pods are created and destroyed depending on the master node instruction a service main task id to track and manage the network for each pod or set of replicas.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

The above example shows a simple service that looks into all pods running with a label called MyApp and designate a TCP network port 9376 to those pods port 80

Ingress

Is kubernetes api to connect the external world to each specific service. 

Finally

Use Kubernetes to enhance your application and platform in web development in Ethiopia