Share this blog!

Hi all!

In this article, I will be discussing on integrating Apex actions in flows and how to pass variables between a Flow and an Apex Action.

Salesforce has taken programming into the next level by introducing Flows, which allows users to configure complex flows in a matter of minutes. However, comfort comes with its own drawbacks as there are certain limitations on what a Flow can do.

For example, if your flow uses nested loops, iterating over 2000 elements or performing over 100 queries is going to exceed the governor limits. The recommended solution for performing complex logic is to use Apex and the good news is, that you can still use Flows and offload only the most complex task to Apex through Apex Actions.

In this article, I will be demonstrating an example scenario, where the requirement is to create junction records between a large set of records.

Assume that we have custom objects to represent Student, Class and they have a many to many relationship. I will be using a Flow and an Apex action to iterate over a set of records and create junction records (Enrolments). The use case is creating enrolment records for a certain semester if the students have made corresponding payments.
As you may notice, this requirement needs nested looping or excessive querying to find out the matching records and gather the required mappings between the enrolling students, payments and classes. This could easily exceed the limits for flows. But we can pass the excessive processing to Apex and still achieve the outcome.


The Flow

The flow we will use is as follows. Note that this is the MVP and the actual scenario can have much complicated flow elements. 

We will be using 'Get Records' elements to gather Student, Class and Payment records. Then the idea is to pass them onto an Apex action and then retrieve the list of Enrolment records. Then use a 'Create Elements' to create the records. 

Let's take a look at the Apex code to be used before digging deep into the Flow element.


Code template

The Apex Code template (the Apex class without any processing logic) is as follows.  


The invoked method: 

The invoked method is the method that is called by the Apex Action in the Flow.

  • The @InvocableMethod annotation allows the Flow to call the method
  • Use the 'label' attribute to define the display name of the Apex Action
  • It can have only one parameter
  • You can have only one invocable method per class


Input parameters:

Although the invocable method can have only one parameter, you can use s custom type to define multiple elements, and then use that custom type as the input parameter. 
  • The @InvocableVariable annotation allows the Flow to set the variables
  • If a custom type is used as input parameter, it has to be a 'List'


Output parameters:

Similar to the input parameters, the output parameters too can be defined using a custom type, where you can define multiple elements.
  • The @InvocableVariable annotation allows the Flow to access the output variables
  • If a custom type is used as output parameter, it has to be a 'List'


public class Enrolments {
    
    @InvocableMethod(label='Get Enrolments' description='Iterate over students, classes and payments and create junction records')
    public static List<EnrolmentsResult> createEnrolments(List<EnrolmentsRequest> request){
        
        //parse inputs and variables
        List<Account> students = request.get(0).students;
        List<Class__c> classes = request.get(0).classes;
        List<Payment__c> payments = request.get(0).payments;
        List<Enrolment__c> enrolments = new List<Enrolment__c>();
        List<String> unenroledStudents = new List<String>();
        
        //start of logic

        //end of logic
        
        //parse outputs
        EnrolmentsResult result = new EnrolmentsResult();
        result.enrolments = enrolments;
        result.unenroledStudents = unenroledStudents;
        List<EnrolmentsResult> resultList = new List<EnrolmentsResult>();
        resultList.add(result);
        return resultList;
    }
    
    
    public class EnrolmentsRequest{
        
        @InvocableVariable
        public List<Account> students;

        @InvocableVariable
        public List<Class__c> classes;
        
        @InvocableVariable
        public List<Payment__c> payments;
    }
    
    public class EnrolmentsResult{
        @InvocableVariable
        public List<Enrolment__c> enrolments;
        
        @InvocableVariable
        public List<String> unenroledStudents;
    }

}



The complete solution with the logic:


public class Enrolments {
    
    @InvocableMethod(label='Get Enrolments' description='Iterate over students, classes and payments and create junction records')
    public static List<EnrolmentsResult> createEnrolments(List<EnrolmentsRequest> request){
        
        //parse inputs and variables
        List<Account> students = request.get(0).students;
        List<Class__c> classes = request.get(0).classes;
        List<Payment__c> payments = request.get(0).payments;
        List<Enrolment__c> enrolments = new List<Enrolment__c>();
        List<String> unenroledStudents = new List<String>();
        
        //start of logic
        for(Account student: students){
            for(Class__c classToEnrol : classes){
                Boolean isStudentEnroled = false;
                for(Payment__c payment: payments){
                    if(payment.payer__c == student.Id && payment.class__c == classToEnrol.Id){
                        Enrolment__c enrolment = new Enrolment__c();
                        enrolment.Class__c = classToEnrol.Id;
                        enrolment.Student__c = student.Id;
                        enrolments.add(enrolment);
                        isStudentEnroled = true;
                        break;
                    }
                }
                if(!isStudentEnroled){
                    unenroledStudents.add('\nStudent: ' + student.Name + ' has not enroled to the class: ' + classToEnrol.Name);
                }
            }
        }
        //end of logic
        
        //parse outputs
        EnrolmentsResult result = new EnrolmentsResult();
        result.enrolments = enrolments;
        result.unenroledStudents = unenroledStudents;
        List<EnrolmentsResult> resultList = new List<EnrolmentsResult>();
        resultList.add(result);
        return resultList;
    }
    
    
    public class EnrolmentsRequest{
        
        @InvocableVariable
        public List<Account> students;

        @InvocableVariable
        public List<Class__c> classes;
        
        @InvocableVariable
        public List<Payment__c> payments;
    }
    
    public class EnrolmentsResult{
        @InvocableVariable
        public List<Enrolment__c> enrolments;
        
        @InvocableVariable
        public List<String> unenroledStudents;
    }

}


Apex Action in the Flow 

Once you have the Apex code in place, you can add the Apex Action Flow element in your flow. You can see that the "label" we defined in our Apex class is the displayed name in the Apex Action.




When you add the Apex Action, you can define the input and output parameters as desired.




Hope you learned something today! Cheers!


Salesforce is an awesome platform to build sites with just a few clicks. I personally love the declarative programming which allow literally anyone (even non-programmers) to use visual components to facilitate complex use cases.

Declarative programming has come a long way since its humble beginnings, yet there are possible improvements that can make the life of the programmer easier. Record cloning is a common use case, which does not have its own element or component in visual flow nor process builder.

The popularly used method to clone a record is to create a record by assigning values from an existing record. If your record has a lot of fields (Salesforce allows up to 800 fields), manually assigning each and every field is going to be a tedious task. In addition, it is not going to be easily maintainable over time.

In this article, I will be discussing of a way to easily clone records using flows. The trick is to use subflows.

The master flow:


Assume we have 2 records as follows, which we wish to clone. Note that the Payment Date is in the year 2019.


Now assume that we have a use case to do the same payments this year. We can simply clone the records and update the Payment Date. In this example, I will be demonstrating cloning of multiple records and will be using loops for that. This approach is also valid for single records but you can choose to omit loops if you wish.

Following is the master flow that we will be using:




The "Get Records" will collect the desired records (this is where you can specify any filters to collect only the desired records). The records obtained will be passed to the Subflow, which will be described next.

The Subflow: 



First, create a variable resource to capture the input record list that is passed from the master flow:



Next create another variable resource to collect the updated records, from which to create records:



Next, we will loop through the records and assign variables to the looped variable.

Assignment-1:

In the first "Assignment Element", we will be setting the fields of the record as follows:


It is important to note:

  1. The "Id" should be set to an empty string
  2. The Payment Date is updated to match our use case

Assignment-2:


In the second "Assignment Element", we will be adding the record to a pre-defined list.




It is important to note: 

You might wonder why we did not use a single assignment element to achieve both the above assignments. In salesforce flows, the updated fields are actually set after the flow completes execution of the element. Therefore, if we used a single assignment element to updated the fields AND add it to the list, the record added to the list is not the updated one, but the previous one itself.

Create records using the list:


Once the records are looped and added to the list, use a "Create Records" element to create records using the list:



You can run the master flow and ensure that your records have been cloned without having to assign each and every field. Note that the Payment Date has been updated and all other fields are intact.


Hope you learned something today.

Cheers!
Hi all,

In this short article (it's mostly screenshots), I will be talking about using declarative methods to perform actions (create records, call a subflow, call approval process etc.) on a timely basis. A quite common example use case is creating quarterly or monthly reports.

In this example, we will be creating a custom record every quarter, that is, the 23rd of March, June, September and December.

For this we will be using the following structure that incorporates the "Start" element that is triggerred by a schedule. Then a "Decision" is determining if the current date is the 23rd of a month divisible by 3. And then a subflow is called, which can be replaced by any supported action you prefer.


Launching the Flow


First we will initiate a Flow that runs every day.

  1. Select "Scheduled Jobs" to launch the flow
  2. Set start date and time
  3. Set the "Frequency" to "Daily"





Formula to check the date

We will have to use a formula to determine whether the current date is a valid date (i.e. 23rd of March, June, September, December).

The custom formula checks if the current date is the 23rd, and also, if the current month is divisible by 3. 

DAY(TODAY()) == 23 && MOD(MONTH(TODAY()), 3) == 0



Decision Element

Once we configured how the flow is launched and the custom formula, we can add a "Decision" element with the custom formula.




That's it! You may activate the flow and it will be performing the configured actions as scheduled.

Cheers!



Hi all!

This is the final of the article series of building a simple JavaScript weather app!

Previous articles:

  1. Part 1 ~ API design
  2. Part 2 ~ Backend Application
  3. Part 3 ~ Frontend Application Basics 

In this article, we will be continuing our discussion on the frontend application mainly focusing on the implementation of components inside the src/components.

components/form.jsx file:

This file corresponds to the "Form" component and following are the main elements in it.

1. Optional error message: Note how error condition is checked and the error is displayed only if there is an error.

2. The title: The title of the application in an <h1> tag

3. The search bar: I used react-select to obtain a searchable select list (More information on react-select). Note how the onSubmit action handles the weather loading functionality.


import React from "react";
import "./form.style.css";
import Select from 'react-select';


const Form = props => {

  return (
    <div className="container">
      <form onSubmit={props.loadweather}>
        <div>{props.error ? error(props.errorMessage) : ""}</div>
        <div className="row">
            <div className="col-md-6 text-left">
                <h1>Weather App</h1>
            </div>
            <div className="col-md-4">
                    <Select
                        placeholder={"Search for a City..."}
                        className={"text-left"}
                        options={props.options}
                        isSearchable={true}
                        onChange={props.selectLocation}
                        name={"location"}
                        defaultValue={props.options.filter(option => option.value === props.selectedLocation)}
                    />
              </div>
              <div className="col-md-2">
                <button className="btn btn-primary">Get Weather</button>
             </div>
        </div>
      </form>
    </div>
  );
};

const error = props => {
  return (
    <div className="alert alert-danger mx-5" role="alert">
      {props}
    </div>
  );
};

export default Form;



components/weather.jsx file:

This component corresponds to the weather section of the application and this encapsulates the current weather condition and the forecasted weather data. When you look into the render returned, you can see how the data is passed into the child components.

Additionally, I used this component to hold 2 methods used by the child components:

  1. getConditionIcon: This method reads the condition name from data and returns the corresponding icon class name.
  2. getReadableDate: This method reads the unix timestamp from the data and returns a formatted date. E.g. converts "1580301692118" to "Thursday, 05/03/2020"


import React from "react";
import CurrentCondition from "./currentcondition";
import Forecast from "./forecast";
import 'react-table-6/react-table.css';
import './weather.style.css';

const Weather = props => {
    console.log(props);
    if (!props.location || !props.data) {
      return <div />;
    }

  return (
      <div>
      <CurrentCondition
        location={props.location}
        data={props.data[0]}
      />
      <Forecast
        data={props.data} />
    </div>
  );
};


export default Weather;

export const getConditionIcon = (conditionName) => {
    const weatherIcon = {
      Thunderstorm: "wi-thunderstorm",
      Hail: "wi-hail",
      Drizzle: "wi-sleet",
      Rainy: "wi-storm-showers",
      Snow: "wi-snow",
      Atmosphere: "wi-fog",
      Sunny: "wi-day-sunny",
      Cloudy: "wi-cloudy",
      Hurricane: "wi-day-hurricane",
      Windy: "wi-windy",
      Tornado: "wi-day-tornado"
    };
  return weatherIcon[conditionName];
}

export const getReadableDate = (dateToRead) => {
    //convert unix to human readabale date
    var d = new Date(dateToRead);
    var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

    var weekDay = days[d.getDay()];
    var date = d.getDate();
    var month = d.getMonth() + 1;
    var year = d.getFullYear();
    var readableDate = weekDay + ", " + date + "/" + month + "/" + year;
    return readableDate;
}



components/currentcondition.jsx file:

This file represents the "CurrentCondition" component that displays the weather of the current timestamp.

The returned rendering divides the row into 3 columns (using col-sm-4) and each data element is fed corresponding data.

The getReadableTime method converts the timestamp value to a readable time format using 12-hour clock. E.g. "19:12:00" is converted to "7.12 pm".



import React from "react";
import {getConditionIcon, getReadableDate} from './weather';

const CurrentCondition = props => {

    const parts = props.location.split(",");
    const city = parts[0];
    const region = parts[1];
    const country = parts[2];

  return (
      <div className="row">
          <div className="col-sm-4  color-card">
              <div className="container">
                  <h4>{city}</h4>
                  <h6>{region}, {country}</h6>
                  <p>{getReadableDate(props.data.forecastDate)}</p>
              </div>
          </div>
          <div className="col-sm-4 color-card">
              <div className="container">
                  <h4>{props.data.conditionName}</h4>
                  <div className="container">
                    <i className={`wi ${getConditionIcon(props.data.conditionName)} display-1`}></i>

                  </div>
                  <h1>{props.data.temperature}&deg;C</h1>
                  <p>
                    <span className="temp-low">{props.data.low} &deg;C</span>
                    &&/&&
                    <span className="temp-high">{props.data.high} &deg;C</span>
                  </p>
              </div>
          </div>

          <div className="col-sm-4 color-card">
              <div className="container ">
                  <p>Humidity: {props.data.humidity}%</p>
                  <p>Visibility: {props.data.visibility}km</p>
                  <p>Pressure: {props.data.pressure}hPa</p>
                  <p>Wind: {props.data.windSpeed}km/h</p>
                  <p>Sunrise: {getReadableTime(props.data.sunrise)}</p>
                  <p>Sunset: {getReadableTime(props.data.sunset)}</p>
              </div>
          </div>
      </div>
  );
};

function getReadableTime(timeToRead){
    var time = timeToRead.split(":");
    var hours = parseInt(time[0]);
    var suffix = "am";
    if(hours > 12){
        suffix = "pm";
        hours -= 12;
    }
    return hours + "." + time[1] + " " + suffix;
}

export default CurrentCondition;



components/forecast.jsx file:

This file corresponds to the table that displays the forecasted weather information. I have used ReactTable (imported from react-table-6) to display the forecasted weather in a tabular format (More information on ReactTable).



import React from "react";
import {getConditionIcon, getReadableDate} from './weather';
import ReactTable from "react-table-6";

const Forecast = props => {

    const data = props.data;

    const columns = [
      {
        id: 'date',
        Header: 'Date',
        accessor: d => getReadableDate(d.forecastDate) // String-based value accessors!
      }, {
        Header: 'Condition',
        accessor: 'conditionName',
        //Cell: props => <span className='number'>{props.value}</span> // Custom cell components!
      }, {
        id: 'conditionIcon',
        Header: 'ConditionIcon',
        accessor: d => getConditionIcon(d.conditionName),
        Cell: row => (
            <span>
              <i className={`wi ${row.value} display-5`} />
            </span>
          )
      }, {
        Header: 'High',
        accessor: 'high',
        Cell: row => (
            <span className="temp-high">{row.value} &deg;C</span>
        )
      }, {
        Header: 'Low',
        accessor: 'low',
        Cell: row => (
            <span className="temp-low">{row.value} &deg;C</span>
        )
      }
];

    const TheadComponent = props => null
  return (
      <div className="row">
        <div className="container color-card">

          <ReactTable
            data={data}
            columns={columns}
            minRows={0}
            showPagination={false}
            TheadComponent={TheadComponent}
            />
        </div>
      </div>
  );
};


export default Forecast;


components/weather.style.css file:

Last but not least, I have used a few custom CSS for styling purposes:



.container {
  margin: 2.2rem auto;
  padding: 20px;
}

.color-card{
    background: rgba(256, 256, 256, 0.6);
}

.container input,
.container ::-webkit-input-placeholder {
  /* color: white !important; */
}
.container input:focus {
  background-color: transparent;
  box-shadow: none !important;
  border: none;
  border-bottom: 2px solid tomato;
}

.col-centered{
    margin: 0 auto;
    float: none;
}

.temp-high{
    color: Tomato;
}

.temp-low{
    color: DodgerBlue;
}

.display-5{
    font-size: 1.5rem;
    font-weight: 300;
    line-height: 1.2;
}

That's it!! After adding some custom CSS, the end result should look like below.



Cheers!


Hello all!

This is part 3 of the article series of building a simple JavaScript weather App.

In part 1, we discussed about the API design perspectives and the architecture of the application. In this article, we will be discussing about the backend implementation of the application, which is essentially about implementing the API we discussed previously.

In part 2, we implemented our backend application which deployed our weather API.

In this article, we will be implementing a front end (react) app that will be calling the APIs and retrieving information and then displaying them in the browser. Since it will be lengthy, we will look at the implementation of each component in the next article and look at the page design and data retrieval in this article.

So the plan is to allow the user to pick a location from a list of supported locations and then display the corresponding weather information in the screen. Additionally, we will use sticky sessions to let the browser remember the user's last-searched location.

If you have react installed already, you can use the following command format to create an application and start it.

npm install -g create-react-app
create-react-app weather-app
cd weather-app
npm start

The project skeleton that will be created would look like the following:


Project
|
|-------node_modules
|
|-------public
|
|-------src
| |-------assets
| |-------components
| |-------config
| |-------service
| |-------App.css
| |-------App.js
| |------- ...
|
|-------.gitignore  
|-------package-lock.json  
|-------package.json


And you guessed it right, we will be altering the files inside the "src" to get our app up and running.

Retrieving the data

We will start by implementing the Rest API calls, but before that, we will configure the Rest API endpoints in the src/config/config.js file:

config.js file:

'use strict'


const config = {
 locationsAPI: "http://localhost:8080/locations",
 forecastsAPI: "http://localhost:8080/forecasts?location="
}

module.exports = config

Once the endpoints are configured, we can call them using the following methods in src/service/apiservice.js file in a class called Api.

The class has 3 methods:

  1. loadLocations - Calls the location endpoint and retrieve a list of locations, format the items in the list (by calling the method explained next) and return the list. Note that we are returning the list in the format {options: ...}, which is used by the react-select component we will be using to display the search options. (More about it in the "Form Component" section below.
  2. formatLocationList - Format the list of locations by concatenating the city, region and country by commas (which could be directly fed into the select box)
  3. loadWeather - Calls the weather endpoint with the selected location and return the result


apiservice.js file:


import Config from './../config/config';

export default class Api{

  //retrieve the list of locations from the API call
  loadLocations(){
      return fetch(Config.locationsAPI).then((response) => {
            if(!response.ok){
                throw new Error(response.statusText);
            }
            else return response.json();
        })
        .then((data) => {
            return {
                options:this.formatLocationList(data),
                error: false
            };
        })
        .catch((error) => {
            return {
                error: true,
                errorMessage: error.message
            };
        });
  }

  //format the retrieved location list to map to required format
  formatLocationList(data){
      //convert result from api into objects with fields "label" and "value"
      var options = data.map(function(val, index){
              return {
                  value:val.city+","+val.region+","+val.country,
                  label:val.city+", "+val.region+", "+val.country
              };
      });
      return options;
  }


  //load the weather of the selected location
    loadWeather(location){

      return  fetch(Config.forecastsAPI+location).then((response) => {
            if(!response.ok){
                throw new Error(response.statusText);
            }
            else return response.json();
        })
        .then((data) => {

            return {
                location: location,
                data: data,
                error: false
            };
        })
        .catch((error) => {
            console.log("Error: " + error.message);
            return {
                location: location,
                data: undefined,
                error: true,
                errorMessage: location + ": " + error.message
            };
        });
  }
}

Components

Now let's take a step back and look at how we want the end application to look like. I like to keep things simple, so I came up with a single page app and the (desktop browser) wireframes look like below:

Since we are going to implement this in react, we need to divide them up into components and I used the following component structure and all the component implementations will be stored inside "src/components" folder:



When you take a look at the above, you can see that there is a search bar for which we will be using a searchable select input type. Once a user picks a location and hits the "Search" button, the related weather information should be displayed as shown above.


App.js file:


So let's start by App.js file, which contains the entire page.

1. constructor(): The constructor method initializes the state and the Api objects. In the state, I have kept track of the list of options (locations), the selected location, the weather data relevant to the location and error details.

2. componentDidMount(): This method is called once in the component lifecycle and is the best place to make API calls. Note that I have used localStorage to check if there is an existing location in the memory, if so, the weather for that location will be loaded. If there is no location in the memory, only the list of locations (the search bar) will be displayed to the end user.

3. getWeather : I have defined an asynchronous method to handle the form submit in the search bar. This method will be fed into the "Form" component as the onSubmit functionality. When the user selects a location and clicks the Search button, this method will be fired. This method will read the selected location, set the localStorage (for sticky sessions), call the weather API and set the state. When the state is updated, the page is rerendered, causing the weather data to be displayed.

4. render(): This method contains the components of the application. Note that we have included only the "Form" and "Weather" components, which are the outermost components of our app, and each sub components will be inside their respective parents. Also take a note on how the data is passed between components.



import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import "weather-icons/css/weather-icons.css";
import "./App.css";
import Form from "./components/form";
import Weather from "./components/weather";
import API from './service/apiservice';



class App extends React.Component {
  constructor() {
   super();
   
   this.state = {
    options: undefined,
    location: undefined,
    data: undefined,
    error: false,
    errorMessage: undefined
   };
   this.api = new API();
  }

  //get the list of locations
  //invoked immediately after a component is mounted
  componentDidMount() {

   //if local storage contains a saved value, load the saved location's weather
   var locationFromStorage = localStorage.getItem('weatherAppLocation');
   if (locationFromStorage) {
    this.api.loadWeather(locationFromStorage).then((response) => {
     this.setState(response);
    });
   }

   //load the list of locations in the dropdown
   this.api.loadLocations().then((response) => {
    this.setState(response);
   });

  }

  //define the function for getting the weather of selected city
  getWeather = async e => {
   e.preventDefault();

   //get the selected value of the dropdown
   const location = e.target.elements.location.value;

   if (location) {
    localStorage.setItem('weatherAppLocation', location);
    this.api.loadWeather(location).then((response) => {
     this.setState(response);
    });

   } else {
    this.setState({
     error: true,
     errorMessage: "Please enter a location"
    });
   }

  };

  render() {
              if(!this.state.options) return null;

              return (
                    <div className="App">
                 <div className="container col-centered">
                      <Form
                       options={this.state.options}
                       loadweather={this.getWeather}
                      selectedLocation={this.state.location}
                       error={this.state.error}
                       errorMessage={this.state.errorMessage}
                      />
                      <Weather
                     location={this.state.location}
                     data={this.state.data}
                      />
                   </div>
                    </div>
              );
           }
}

export default App;

Let's dig deep into each of the components in the next article! Cheers!
Hello all!

This is part 2 of the article series of building a simple JavaScript weather App.

In part 1, we discussed about the API design perspectives and the architecture of the application. In this article, we will be discussing about the backend implementation of the application, which is essentially about implementing the API we discussed previously.

In this tutorial, we will be:

  1. Initiating a node project
  2. Mapping API endpoints to functions
  3. Configuration and connecting to a database
  4. Querying the database and return result
  5. Testing the API

Things we will NOT be talking about and act as prerequisites:

  • Installing node, MySQL etc.
  • Setting up MySQL or generating mock data
  • Installing packages


A quick recap from the last article, the API we came up with has the following endpoints and use cases:

  1. GET   /locations - Retrieve list of locations that can be forecasted ordered alphabetically by the city
  2. GET   /conditions - Retrieve list of supported weather conditions
  3. GET /forecasts - Retrieve weather forecast of a city defined by query parameter, responding with an array of 10 forecast items. (Optional "limit" query parameter can define the expected forecast count)


Initializing the project

You can simply use npm init to start a node project.

In addition to the usual node project structure, I have added some extra packages so that the services, db management and configurations are separately stored.

Project 
|-------api 
|        |-------swagger.yaml
|-------config 
|        |-------config.js 
|-------db 
|        |-------db.js 
|-------node_modules 
|-------service 
|        |-------condition.service.js 
|        |-------forecast.service.js 
|        |-------location.service.js 
|-------.gitignore 
|-------index.js 
|-------package-lock.json 
|-------package.json

Swagger definition

The content of swagger.yaml can be found in this gist (https://gist.github.com/sachi-d/0f2c0af614723aab0c30095fdfcbe93c) and it was added to the project as a reference to the API definition we came up with. 

You can use swagger to generate the server code from the yaml, but in this tutorial, we will be writing the API functionality by hand simply because our API is not that complicated.

Index.js

The index.js file contains the mappings of the API endpoints and it is essentially the starting point of the application. 

You may notice that the endpoints are mapped into functions in the service layer and finally, the app is listening to the port defined in the configuration. Note how the headers are being used to enable CORS.


const express = require('express');
const app = express();
const mysql = require('mysql');
const config = require('./config/config.js');
const db = require('./db/db.js');
global.db = db;


const {getLocations} = require('./service/location.service');
const {getConditions} = require('./service/condition.service');
const {getForecasts, getForecastsByID} = require('./service/forecast.service');

//enable CORS
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

//map the endpoints with the functions
app.get('/locations', getLocations);
app.get('/conditions', getConditions);
app.get('/forecasts', getForecasts);


const port = config.port;
app.listen(port, () => console.log(`Listening on port ${port}..`));

Configuration file

My config.js file content are as follows, but you can add any kind of configuration/variable options in this, so that you can easily access/update them on the run.

'use strict'

const config = {
 port: process.env.PORT || 8080,
 db: {
  host: 'localhost',
  user: 'mydb-username',
  password: 'mydb-password',
  database: 'mydb-database-name',
  multipleStatements: true
 }
}

module.exports = config

Database connection

I used the db.js file as the database manager, to handle the connection creation.

const mysql = require('mysql');
const config = require('./../config/config.js');

const db = mysql.createConnection (config.db);

// connect to database
db.connect((err) => {
    if (err) {
        throw err;
    }
    console.log('Connected to database');
});

module.exports = db;

Endpoints

I used the service package to collect the data from the database and then expose them through the endpoint. Alternatively, you can achieve more coherency by using a separate DAO layer to retrieve the data and then using a service layer to handle the business logic.

The following code represents the SQL query execution, which retrieves the list of supported weather conditions and locations and then responds to the API requests with the collected data. 


condition.service.js


module.exports = {
 getLocations: (req, res) => {

  //retrieve a list of available locations
  let query = "SELECT * FROM `location` ORDER BY city";

  // execute query
  db.query(query, (err, result) => {
   if (err) {
    console.log(err);
    res.status(400).send("An unexpected error occurred while retrieving locations.");
    return;
   }
   res.send(result);
   return;
  });
 }
};


location.service.js


module.exports = {
 getConditions: (req, res) => {
  //returns a list of supported weather condition names and IDs
  let query = "SELECT * FROM `weathercondition` ORDER BY id ASC";

  db.query(query, (err, result) => {

   if (err) {
    console.log(err);
    res.status(400).send("An unexpected error occurred while retrieving weather conditions");
    return;
   }
   res.send(result);
   return;
  });
 },
};



forecast.service.js

The forecast endpoint implementation seems like the most complicated of all, but when you break down the functionalities, you can easily understand what is happening in the code.

The endpoint requires the location as a query parameter which should be defined by the city, region and the country, separated by commas (inspiration was from Yahoo weather API).

For example,

/weaptherAPI/forecasts?location=Sydney,NSW,Australia 

is a valid API call while

/weaptherAPI/forecasts?location=Sydney,Australia 
/weaptherAPI/forecasts?location=NSW,Australia 
/weaptherAPI/forecasts?location=Sydney

are invalid API calls.

Optionally, the limit query parameter can be used to limit the number of results retrieved, if limit is not defined, 10 results will be returned by default.

When analysing the following code, you may observe how the query parameters are parsed and validated. I have used error messages to handle validation failures. If validations are successful, the parameters will be fed into the SQL query which would return the results as an array.


module.exports = {
 getForecasts: (req, res) => {
  //returns a list of weather forecasts for the specified location

  //if limit is not set, return 10 results by default
  const limit = req.query.limit || 10;
  if(isNaN(limit)){
   res.status(400).send('Invalid request: Invalid limit parameter');
   return;
  }

  const location = req.query.location;

  //location is a required parameter
  if (!location){
   res.status(400).send('Invalid request: Missing location parameter');
   return;
  }


  const parts = location.split(",");

  if(parts.length != 3){
   res.status(400).send('Invalid location parameter');
   return;
  }
  const city = parts[0].trim();
  const region = parts[1].trim();
  const country = parts[2].trim();


  let query = `SELECT w.*, c.conditionName FROM
          (SELECT weather.* FROM weather, location WHERE idLocation = location.id
     AND location.city = "${city}"
     AND location.region = "${region}"
     AND location.country = "${country}"
            AND forecastDate >= UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY) * 1000
            ORDER BY forecastDate
            LIMIT ${limit}) as w
          LEFT JOIN
            (SELECT id as conditionID, name as conditionName FROM weathercondition) c
            ON w.idCondition = c.conditionID`;


  // execute query
  db.query(query, (err, result) => {
   if (err) {
    console.log(err);
    res.status(400).send("An unexpected error occurred while retrieving weather forecasts");
    return;
   }
   if(result.length == 0){
    res.status(404).send(`No data found for ${location}`);
    return;
   }
            res.send(result);
  });

 }
};



Testing the API

Now your application is ready to run. Use node index.js to start the application in your machine and since all the endpoints are GET type, you can simply use the browser to view the results. 

Access the following URLs to view your results in the browser:

  • http://localhost:8080/locations
  • http://localhost:8080/conditions
  • http://localhost:8080/forecasts?location=any+location+you+have+dummy+data+of

When implementing an API that can be called by a third party application, it is quite important to handle all possible scenarios. These scenarios include both valid and invalid inputs which need to be handled carefully in your implementation. In order to test your application, it is essential to list down the test scenarios that are applicable. The following depicts some of the test scenarios that need to be tested against our application.



Once the test scenarios are identified, testing needs to be carried out. This can be done either manually or programmatically, which we will talk about in a future article.

That's it for the the backend implementation and feel free to share anything you would find useful in the comment section.

Cheers!


Introduction

Hello all! 

This will be the first of the article collection that we will be going through to implement a simple weather app using JavaScript for the full stack. The weather app would have:

1. A Rest API that would give us current and forecasted weather for predefined locations
2. A web application that would display the current and forecasted weather of a selected location

To achieve the above, we would need to make some decisions. 

Since we are going to pursue everything using JS, we could easily use NodeJS to implement a backend server that would deploy the Rest API. For the front end app, we do have a lot of choices, but in this tutorial, we will be using React. We also need some sort of DB integration to read the weather data from. To limit the scope of this tutorial, let's skip how the data is entered and worry only about reading data from the database. 

In this tutorial, we will be talking about the following topics:

1. Part 1 - The architecture and API design
4. Part 4 - Front end implementation ~ Components


In Part 1, we will be discussing about the architecture and the design considerations of the API. We will be talking about:

  1. A proposed architecture for the backend and client apps
  2. A little introduction to Rest APIs
  3. Proposed API endpoints and their parameters and responses

The Architecture

As mentioned above, the purpose of this tutorial is to implement a simple weather app using JavaScript. It might sound like one big app, but it could be easily reusable and made more independent if we have 2 different apps for the backend and frontend respectively. Implementing a Rest API would basically mean the same thing.

In terms of the data, we could integrate a database and expose the data in the Rest API, which can be called by any third party app and display the data any way they like. Think of our backend as something similar to the Yahoo or the OpenWeatherMap API.

So the architecture I would go for would be as follows:


As you can see, the 3 main components would be our backend, frontend and the DB. 

Starting the implementation of the client app first would not be a very good idea, because you would not have an idea about the data you would be getting from the APIs. So the best course of actions (or the TODO list) would be as follows:

1. Design the APIs
2. Implement the APIs (backend)
3. Implement the client App

So let's get ready to tick-off our first to-do item!

API Design

If you're an expert on Rest APIs, you may skip the following section, but everyone else, gather up for a very brief introduction to Rest APIs and the bits of information you should absolutely know about Rest APIs.

A RESTful API is simply an application program interface (API) that uses HTTP requests to exchange data (communicate) between components. These communications can be of the following types:
  • GET - read data
  • PUT - update data
  • POST - insert data
  • DELETE - delete data

Restful APIs should always comply to the following constraints:
  1. Use of a uniform interface (UI) - The purpose of Rest APIs is to communicate with servers to get/update information on resources. And this constraint simply means that any resource should be defined using URIs (uniform resource identifiers) that should be self descriptive. For example, if an API is used to retrieve details of a book "/books/1234" is resource based and is a better design than "books/the+book+I+bought+today" as the latter doesn't seem like a uniform identifier for a book.
  2. Client-server based - There should be a clear delineation between the client and server. UI and request-gathering concerns are the client’s domain. Data access, workload management and security are the server’s domain. This loose coupling of the client and server enables each to be developed and enhanced independent of the other.
  3. Stateless operations - All client-server operations should be stateless, and any state management that is required should take place on the client, not the server.
  4. RESTful resource caching - All resources should allow caching unless explicitly indicated that caching is not possible.
  5. Layered system - REST allows for an architecture composed of multiple layers of servers. So it is valid if a client app doesn't know if he is directly calling a server or if it goes through multiple layers that would finally land on the server.
  6. Code on demand - Most of the time a server will send back static representations of resources in the form of XML or JSON. However, when necessary, servers can send executable code to the client.

Now let's think about how the weather APIs could be used and what kind of data to be exposed.

If the end user has the ability to select a location, he should be provided with a list of locations to choose from. Therefore we need one API to list the locations.

Secondly, when the end user picks a location, the current and the forecasted weather should be delivered, therefore the second API we need is the weather API which is bound to a selected location.

Additionally, I thought of letting the developers know the list of supported weather conditions, so that they can easily configure the icons and what not to show on their client applications. Therefore an additional weather condition API too would be implemented.

Considering all of the above, the following are the APIs that I came up with:

  1. GET   /locations - Retrieve list of locations that can be forecasted ordered alphabetically by the city
  2. GET   /conditions - Retrieve list of supported weather conditions
  3. GET /forecasts - Retrieve weather forecast of a city defined by query parameter, responding with an array of 10 forecast items. (Optional "limit" query parameter can define the expected forecast count)
You can use editor.swagger.io to interactively view the full API definition (with the input/output definitions) in yaml is hosted in Github, which is added in the end of this article to avoid clutter.

In order to make things easier, I designed the database to match the API design, which has exactly 3 tables following the ERD (make sure to populate dummy data to view in the app):

That will be all for now, and let's talk about implementing the backend in the next chapter!

The complete API definitions:

You can use editor.swagger.io to interactively view the full API definition (with the input/output definitions) in yaml is hosted in Github.


Next PostNewer Posts Previous PostOlder Posts Home