How to Get SMS Text Messages From Your WordPress Forms

Do you want to get SMS text messages from your WordPress forms?

Receiving text messages on your phone makes it easy to stay on top of new form submissions, leads, product orders, registrations, and more. This can improve your response rate and increase customer satisfaction.

In this article, we will show you how to easily get SMS text messages from your WordPress forms, step by step.

Get SMS text messages from your WordPress forms

Why Get SMS Text Messages From Your WordPress Forms?

Getting SMS text messages from your WordPress forms lets you receive instant alerts on your phone. This can be very useful for small business websites with smaller teams.

For instance, if you run a restaurant website, then you may want to get an instant alert for food order forms.

Similarly, if you have an appointment or booking form on your website, then an instant text message can notify you of a new booking.

This can help improve customer satisfaction by helping you respond to your customers as quickly as possible.

Having said that, let’s see how to easily get SMS messages from your WordPress forms.

How to Get SMS Text Messages From Your WordPress Forms

You can easily get text messages from your WordPress forms by connecting WPForms with Twilio.

WPForms is the best contact form plugin on the market, used by over 6 million websites. Similarly, Twilio is a leading text messaging service for sending SMS texts.

Since our goal is always to show no-code solutions for beginners, we will be using Zapier to act as a bridge between the two apps. This will let you set up everything without having any coding knowledge.

Ready? Let’s get started.

Setting Up Your WordPress Form Using WPForms

First, you need to install and activate the WPForms plugin. For more details, see our step-by-step guide on how to install a WordPress plugin.

Note: WPForms has a free version. However, you will need at least the Pro plan of the plugin to unlock the Zapier addon.

Upon activation, visit the WPForms » Settings page from the WordPress admin sidebar to enter the plugin license key.

You will find this information in your account area on the WPForms website.

Entering the WPForms license key

Once you have done that, it’s time to create your first form. To do this, head over to the WPForms » Add New page from the WordPress dashboard.

This will take you to the ‘Select a Template’ page, where you can start by typing a name for the form that you are creating.

After that, you can select any of the premade templates offered by WPForms by clicking the ‘Use Template’ button.

For this tutorial, we will be creating a simple contact form.

Choose the contact form template

The template of your choice will now be launched in the WPForms form builder, where you will notice a form preview on the right with its settings in the left column.

From here, you can drag and drop any form field from the left column into the form.

For detailed instructions, you can see our tutorial on how to create a contact form in WordPress.

Configure form entry

Once you are satisfied, just click the ‘Save’ button at the top to store your settings.

Now, to add your WordPress form to your website, open a page/post from the WordPress dashboard. Then, you need to click the add block ‘+’ button in the top left corner of the screen to open the block menu.

Find and add the WPForms block to the page, and then choose the form that you created from the dropdown menu in the block itself.

Add the WPForms block

Finally, click the ‘Publish’ or ‘Update’ button at the top to save your changes.

Once you have done that, you will need to submit a test entry in order to set up Zapier. Here’s our test entry in our custom form:

Add a form entry

Preparing to Connect WPForms and Zapier

We are now going to use Zapier to connect WPForms and Twilio. To do this, you will first need to install and activate the WPForms Zapier addon.

For this, visit the WPForms » Addons page from the WordPress dashboard and find the Zapier addon.

Once you have done that, click the ‘Install Addon’ button.

Add Zapier addon

The Zapier addon will now be installed and activated for you.

Now, you need to visit the WPForms » Settings page from the admin sidebar and switch to the ‘Integrations’ tab.

Here, just click on the Zapier logo to see your Zapier API key.

You will need this key in a later step to connect Zapier to WPForms. Go ahead and copy it somewhere safe, or just leave the tab open.

Copy the Zapier API key

Preparing to Connect Twilio and Zapier

Now, it’s time for you to create a Twilio account. Twilio is a leading SMS service, and they offer a limited free account that you can use.

First, you need to visit the Twilio website and click the ‘Start for free’ button.

Sign up for your Twilio account

You will now be directed to a new screen where you have to create a free Twilio account by providing login details.

Once you have set up an account, you will be sent a verification email that you will need to click on to activate your account.

View Twilio email to verify your account

Once you have verified your account and filled in some additional details, your Twilio dashboard will open up on the screen.

From here, you will have to choose a phone number that will send you text messages every time someone fills out a form on your WordPress website.

To do this, click the ‘Get phone number’ button on the dashboard.

Click the Get Phone Number button

Then, the phone number will be automatically displayed instead of the button by Twilio.

Next, you have to scroll down to the ‘Account Info’ section, where you will see your Twilio Account SID and Auth Token. You will need these when you set up Zapier. You could copy them to a safe place or leave your tab open.

Copy Twilio account information

Creating a Zap to Send an SMS When Your Form is Submitted

Zapier is an automation tool that can be used to connect different apps and services so that they can automate repetitive tasks.

In this tutorial, we will be connecting WPForms and Twilio using Zapier.

To do this, you need to sign in on the Zapier website. If you don’t have an account, then simply create a free one.

Once you have set up an account, click the ‘+ Create Zap’ button in your dashboard.

Click Create Zap button

Note: Zapier uses the word ‘Zap’ for a process that includes a trigger and an action. Our trigger will be someone submitting the form. Our action will be sending an SMS message.

You will now be taken to a new screen where you can start by typing a name for your Zap.

Then, you need to click on the ‘Trigger’ tab to set up WPForms as the trigger.

Click the Trigger option

This will open a prompt on the screen where you have to type ‘WPForms’ into the search bar.

Now, once the WPForms icon comes on the screen, simply click on it.

Choose the WPForms as the trigger integration

Upon choosing WPForms as the trigger integration, you must select the event that will actually trigger the action.

To do this, select the ‘New Form Entry’ option from the ‘Event’ dropdown menu in the right column. After that, click the ‘Continue’ button.

Choose form entry as the trigger event

Zapier will now prompt you to connect to WPForms.

Go ahead and click the ‘Sign in’ button to continue.

Click Sign in button next to the WPForms option

This will launch a new window on your screen where you have to provide your WPForms API key and your website URL (domain name).

Once you have done that, just click the ‘Yes, Continue to WPForms’ button.

Add the WPForms API key

Now that you have connected your WPForms account with Zapier, you must choose a WordPress form from the ‘Form’ dropdown menu in the right column.

After that, click the ‘Continue’ button. Now, every time someone fills out this WordPress form, you will receive an SMS alert.

Choose a trigger form froom the dropdown menu

Next, Zapier will ask you to test your trigger. This will use the test data that you submitted through your form.

Go ahead and click the ‘Test trigger’ button.

Click the Test Trigger button

You will now see the data from the demo form entry that you submitted earlier on your website.

From here, click the ‘Continue with selected record’ button to move forward.

Click Continue with selected record button

This will open the ‘Change Action’ prompt on the screen, where you need to type Twilio into the search bar.

Upon finding this tool, simply click on the Twilio icon.

Choose Twilio as action integration

Next, select the action that Twilio will take every time a WordPress form is submitted.

From here, select the ‘Send SMS’ option from the ‘Event’ dropdown menu in the right column and click the ‘Continue’ button.

Select the Send SMS option as the action event

In the next step, you will have to connect your Twilio account with Zapier.

To do this, click the ‘Sign in’ button next to the ‘Connect Twilio’ option.

Click the Sign in button next to the Twilio

This will open a new window on the screen where you have to provide your Twilio account SID and Auth token that you copied earlier.

On providing the details, click the ‘Yes, Continue to Twilio’ button to move ahead.

Add Twilio Account SID and Auth token

You now need to customize your SMS message settings from the right column.

First, click on the ‘From Number’ dropdown menu, where you should see the free phone number from your Twilio account. Simply select this.

After that, type your own phone number into the ‘To Number’ field. This will be the number where you will receive SMS notifications.

For the message, you can type in any text you like. You can also select from your WPForms input fields.

For example, you can add the Name, Email address, or Comment fields to the message. This means that the SMS message you get will show these details from the form submission.

After that, click the ‘Continue’ button at the bottom to move ahead.

Customize SMS message

Zapier will now show you the data that will be sent through your Zap.

From here, click the ‘Test step’ button to test the Zap that you have created.

Click the Test Step button

You should now see a message on the screen saying that the SMS was successfully sent.

You should also check your mobile phone to see if you received the SMS message.

SMS message

Once you are happy, simply click the ‘Publish’ button and then toggle the switch at the top to ‘Active’.

Your Zap has now been activated, and you will now get SMS text messages from your WordPress forms.

Publish your Zap

Bonus Tip: Send SMS Messages to Users to Increase Engagement

SMS is a powerful marketing tool that can help you communicate with your customers and increase sales. Apart from receiving form submission messages, you can also send SMS to users to increase engagement on your WordPress site.

For example, if you have an online store, then you can send SMS notifications to your customers for order confirmations, cart abandonment, and more.

You can even offer them coupon codes or alert your customers that their favorite products are back in stock.

This can help open a two-way channel of communication with users. It can even increase conversions because SMS messages have open rates of over 98% and click-through rates of over 36%. This is significantly higher than other marketing channels, including email marketing.

For more detailed instructions, please see our tutorial on how to send SMS messages to WordPress users.

We hope this article helped you learn how to get SMS text messages from your WordPress forms. You may also want to see our comparison of the best business phone services and our guide on how to create a free business email address.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Get SMS Text Messages From Your WordPress Forms first appeared on WPBeginner.

Create a Free Track and Trace COVID-19 Form for Businesses with Forminator

Whether you or your client runs a cafe, boutique, hair salon, a place of worship, or practically any business or organization, with the help of our 5-star rated plugin, Forminator, you can track & trace and help manage COVID-19.

We’ve developed an easy-to-implement track and trace template that you can use or add to your clients’ websites using Forminator.

It works by collecting personal data to slow the spread, following government guidelines.

Image of Forminator with a track and trace sheet.
Forminator is masked-up and ready for you and your clients to track and trace!

This straight-forward tutorial will show you how to set up and implement a track and trace form in just a few minutes (or less).

We’ll be going over:

  • How to Quickly Set Up a Track and Trace Form
  • Easily Keep Track of Check-ins
  • Using a QR Code to Link to Form

How to Quickly Set Up a Track and Trace Form

Creating a track and trace form can be created and published with just a few steps.

From Forminator’s dashboard, create a new form to get started.

The create a new form button.
You’re one click away from a new form.

For this form, a blank template will do. Give it a name, and then you’re ready to add Fields.

This simple form just needs the essential fields for track and trace information. We’re suggesting to add the fields for:

  • Name
  • Phone Number
  • Email
  • Number
  • Datepicker
  • GDPR
Where you choose what fields to include.
Add any additional fields that you’d like.

Hit Insert Fields when you have all of the fields you’d like to include ready. You can always add or remove fields at any time past this point.

Some of the information we want to have mandatory before a user can submit the form. To do that, just go to the individual fields required to be filled out, click on Settings, and select the Required option.

Add the mandatory option to as many fields as you’d like.

Once you make it required, a little red Asterisk appears next to that field. In this track and trace form, we have the name, number, and date. The GDPR is automatically set up as a required field.

We’re also going to want to change the text to some of these fields.

To start with, the Number field will have the caption Number of people in your party. For the Date field, we’ll add the text Date visiting. And finally, instead of Send Message, we’ll change that to Check-in in the Send Message field (automatically included in the form).

To do this, simply click on the field, and under the Labels tab, type in any text that you want to include.

Where you add the label for the form.
You can also add a custom placeholder, default value, and description.

Arrange the fields any way you want. Forminator’s drag and drop feature makes it a breeze.

Have several fields on one line or separate – the choice is yours!

Once you have the form sorted, hit Preview to get a glimpse of what it looks like.

A preview of the form.
With a preview, you may notice some changes you want to make.

When it comes to the GDPR, you can change the text inside the Privacy Policy and Terms and Conditions by clicking on Labels in the field.

Since this form is designed solely for COVID-19 track and trace, you must follow protocol and legal guidance set forth by your local governing body for the collection of personal data, ensuring compliance with GDPR.

Some good points to include in the GDPR can consist of:

  • Stating that this form maintains a minimum amount of data for the shortest duration of time as possible.
  • Ensuring customers are informed about how the data is used and secured safely.
  • That data is deleted securely after the duration set and limit the use of the data.
  • Information about following all legal obligations by law, for example, GDPR in the EU, or specific advice set out by your governing body (you are solely responsible for this).

Another feature to include on this form is the user’s message displayed once the form is submitted.

Under Edit Form>Behavior, you can quickly change the Inline Message to something along the lines of Thank you for checking-in.

Where you add a personalized message a user will receive once submitting form.
Create any personalized message that you’d like.

You can also integrate the form with the most popular 3rd party apps, such as MailChimp. We have some detailed information about doing that with Forminator in this article.

If there’s nothing else to do with your form and you have all the text edited, GDPR information included, and your form looks good, you’re ready to publish and start the track and trace process.

When you hit Publish, Forminator provides you with a shortcode that you can paste into any page, post, or acceptable widget in WordPress.

Forminator's shortcode.
When Forminator gives you the thumbs-up, your track and trace form is ready for use.

Easily Keep Track of Submissions

You can easily access the information in Forminator’s dashboard by clicking View Stats under the form’s name to view submissions and keep tabs on tracking and tracing.

Where you'll view your form's stats.
Click on the little icon to view stats.

From here, Submissions are just a click away.

You’ll see all of the submissions that have been made by Date Submitted, Name, Email Address, and the additional fields. Plus, you have options to export the submissions, delete entries, search by date, search by keyword, and get detailed information.

Where all of the form's submissions are located.
All the form submissions are in one place.

To view more detailed information, including what’s on the additional fields, click the dropdown by each name.

All the detailed information is accessible here.

Also, you can set up email notifications that will go to you and anyone that checks-in.

Simply go to Edit Form and Email Settings. Here, you can add a customized message, choose which admin email notifications get sent to, and edit how you’d like the email to appear to users.

Use the form data dropdown to add data to the body of your email.

You’re now all set to track and trace with all the necessary data needed.

Want to see it in action? Check out a live example of a track and trace form here.

Using a QR Code to Link to Form

To help with tracking and tracing, you can make it easy for users to get to the form by setting up a QR code that links back to it.

There are multiple ways of including one with the help of a plugin.

For example, the Kaya QR Code Generator plugin.

Kaya QR Code Generator.
The Kaya QR Code Generator is one of many options.

It functions by creating QR codes through a widget or a shortcode, so you can easily insert it into any page, post, or sidebar.

You can browse through numerous QR Code plugins here and find the best one that works for you and your client.

Ace the Track and Trace

With the help of Forminator, you can ace the track and trace. As you can see, it’s quick, easy to manage, and free to do. Your clients will benefit and it can help slow the spread.

With Forminator, there’s a lot more you can do as well that can help with the pandemic. This includes creating contact forms to keep customers in the loop of reopenings, collecting donations, registering for social distancing events, and more. If you’re not familiar with all of his capabilities, be sure to check out how to get the most out of Forminator here.

Soon enough, together, we’ll be able to stop this virus and get on with a bit more normalcy.

 

Complete List of WAI ARIA Roles and Their Uses

Web accessibility is the process of making your website equally usable to people of all abilities and disabilities. It’s about understanding that users with disabilities may use assistive technologies to access the web. As a website owner, it is your duty to ensure that you make your website adequately compatible with such assistive technologies. WAI ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) is a set of guidelines published by the W3C. It defines additional HTML attributes that you can apply to your tags. These attributes help improve accessibility and make your DOM structure more readable by assistive technologies.

How To Manage File Uploads In React With Google Storage And GraphQL

By leveraging React-Apollo, this article focuses on how a file upload functionality can be added to a new or existing front-end application being powered by a GraphQL API. To achieve this, we would build this demo application which allows users to upload a profile image when creating an account alongside their preferred username. While we do this, we would gradually work through the process of :

  • Creating a Node GraphQL backend application capable of accepting and sending the uploaded file to a Storage Bucket within the Google Cloud.
  • Setting up a connection to the Google Cloud Storage.
  • Collecting files inputs in a React Application and sending them to a GraphQL backend application using React Apollo.

Note: Although all code snippets are explained, to fully understand them you should have an understanding of JavaScript's es6 syntax, GraphQL and React.js.

This article will be beneficial to developers who are interested in or considering using Google Cloud Storage for file uploads in their React and Nodejs GraphQL application. While this article is not an introduction to GraphQL, each GraphQL concept used within this article is explained and referenced for better understanding.

Setting Up A Node GraphQL API

We will be building a GraphQL API to be consumed by our React application. This backend application will receive the image uploaded by a user and send the uploaded file to Google Cloud Storage.

To begin, we use the Apollo-Server-express and Express.js library to quickly bootstrap a GraphQL API. We can do this by running the following commands :

# Create a new Project folder and( && ) move into it
mkdir Node-GraphQL-API && cd Node-GraphQL-API

# Create a new Node project
yarn init -y

# Install the two needed dependencies 
yarn add apollo-server-express express

Next, we proceed to build a single GraphQL endpoint, which is accessible via port 4000.

const express = require('express')
const { ApolloServer } = require('apollo-server-express')

const { Queries , Mutations , TypeDefs } = require('./resolvers') 

const resolvers = {
  Query : Queries , 
  Mutation : Mutations 
} 

const server = new ApolloServer({ TypeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

app.listen({ port: 4000 }, () =>
  console.log(Graphiql running at http://localhost:4000/${server.graphqlPath}));

We started by importing our queries, mutations and type definitions from the resolvers file, then we created a resolvers object containing the imported queries and mutations then passed it into the ApolloServer constructor alongside the imported type definition.

Next, we created an instance of express.js in the app variable and integrated it into apollo server by calling the applyMiddleware method. According to react-apollo’s documentation on the applyMiddleware method, this integration enables the addition of various small internal middlewares. Lastly, we called the listen method on the express instance, telling it to listen and serve HTTP connections on port 4000. We also added a callback to log out a message telling users the server has been started.

The Graph Query Language is strongly typed and this is where most of it’s auto-documenting feature comes from. This strong typing is achieved using the GraphQL Schema definition language. It is also what is used to specify the data resolved by the Query, Mutation and Subscription operations.

A practical example of this is our schema definition for our upload application below.

const { gql }  =  require('apollo-server-express')

const typeDefinitions  = gql` 
  type File {
    filename: String!
    mimetype: String!
    encoding: String!
  }

  type User {
     username: String
     imageurl: String
  }

  type Query { 
    getUser  : User
  }

  type Mutation {
    createUser ( 
      username : String!
      image : Upload!
     ) : User

    deleteUser () : Boolean!
   }
`
export default typeDefinitions

Above, we created a schema using gql, consisting of three types; the File and User types which are object types in the GraphQL Schema Definition Language and the Query and Mutation types respectively

The created File object type contains three string fields; filename, mimetype and encoding which are all typically contained in any uploaded file. Next, we created an object type for Users with two string fields; username and imageurl. The username field is the username typed in by a user when creating an account, while the imageurl is the url of the image uploaded to the Google Cloud Storage. It would be used passed into the image src attribute to render the stored image to the user.

Next, we create the Query type which defines the query resolver function we have in the application. In our case, it is a single query used to get the user’s data. The getUser query here returns all data in the User object type.

We also created the Mutation type, which defines the two following mutations resolver functions below;

  • The first one createUser takes in a username which is a string scalar type and an Upload input type which comes from React-Apollo. It returns all data contained in the User object type after a successful account creation
  • The second one deleteUser takes in no argument but returns a boolean value to indicate if the deletion was successful or not.

Note: The exclamation mark (!) attached to these values make them mandatory, meaning that data must be present in that operation.

Implementing Resolver Functions

Having written a schema which defines the resolver function in our application, we can now go ahead in implementing the functions for the resolvers which we previously defined in the schema.

We start with the getUser resolver function which returns the user’s data.

// stores our user data
let Data  = []

export const Queries = {
   getUser: () => {
      return Data
  }
}

We created a data array which stores the user’ data. This data array is to be used by both the mutation and query function and so it is declared globally. Next, we implemented the getUser function which returns the array containing the user’s data when queried.

Mutating Data

In Graphql applications, CREATE, UPDATE and DELETE operations are performed through the use of the Mutation resolver functions, they are what mutate the data.

An example of these mutation resolvers are the two resolvers in our application which creates a user and deletes a user.

export const Mutations = {
    createUser: (_, { username, image }) => {
      # boilerplate resolver function
   },

 # resets the user's data 
  deleteUser: (_ ) =>  {
    Data = []

    if (Data.length < 1) {
        return true
    } else {
        return false
    }
 },
}

Here is an explanation of the two resolvers above:

  • createUser
    This creates a user using the passed in arguments. First, we specify the parent argument (_) and next we destructure the username and image which would be passed in when making the mutation in our frontend application.
    This is where the uploading of files will take place. We will come back to the actual implementation of this mutation resolver after setting up a connection to the Google Cloud Storage.
  • deleteUser
    As we defined it in our schema, this resolver function takes no argument. The purpose is to empty the data array and by checking the length, it returns a boolean value; - true if the items are less than 1, meaning the array is empty and false if not.
    Note: If we had a real database connection, this resolver function would take in an ID argument which would be used in selecting the user whose record is to be deleted.

Having created our schema and resolver functions, we can now start our node server and test it by making HTTP requests using curl at http://localhost:4000/graphql or more conveniently, using the offline GraphiQL web console on http://localhost:4000/graphql just as shown below:

Setting Up The Google Cloud Storage

The Google Cloud Storage, an online file storage service is used to store object data. It is flexible enough to serve the needs of either enterprise grade applications or personal projects such as this. Being one of the offerings of the Google Cloud Platform, it can be found within the Storage section of the Google Cloud Console.

To get started, follow the following steps :

  1. Visit the Google Cloud Platform to create an account and a project.
    (First time users are given $300 worth of GCP credits so which is more than enough for this demo project.)
  2. Visit the Storage Browser section, within the Google Cloud Console and click on the Create Bucket button within the top navigation pane.
  3. Enter a preferred bucket name, leave other settings as default and click the create button at the bottom of the list.

After being created, we would be redirected to the empty bucket similar to the one below;

At this point we have created a bucket where the uploaded files would be stored. Next we need a Service Account in order to enable a communication between our Node server and the Google Cloud.

What are Service Accounts?

Service accounts are a special type of account on the Google Cloud, created for non-human interaction, meaning communication through APIs. In our application, it would be used with a service account key by our API to authenticate with the Google Cloud when uploading stored user's images.

We follow the following steps in order to create a service account.

  1. Open the Identity Access Management ( IAM ) section of the Google Cloud Console
  2. From the left side navigation bar, click on Service Accounts and when there click on the Create Service Account button.
  3. Enter a preferred name and a description and click the Create button. We would see a service account ID being auto generated using characters from our typed in name.
  4. Next, click the Select Role dropdown menu to select a role for this service account.
  5. Type “Storage Admin” and click the Storage Admin role. This role gives our Node server a full control over stored resources in our storage buckets.
  6. Leave the remaining fields blank and click on the Done button.
After being created, we would be redirected to a list of all Service Accounts within our project, including the default created ones and the newly created service account. 

Next, we need to create a secret Service Account Key in JSON format. The following steps below outline how to do that;

  1. Click on the newly created Service Account to get to the page for this Service Account.
  2. Scroll to the Keys section and click the Add Key dropdown and click on the Create new key option which opens a modal.
  3. Select a JSON file format and click the Create button at the bottom right of the modal.

After creating that, the key would be downloaded locally to our device and we would see an alert telling the user to keep the key private. This is because it contains sensitive fields about our project on the Google Cloud Platform. Below is an example of the contained fields :

 {
  "type": "service_account",
  "project_id": "PROJECT_NAME-PROJECT_ID",
  "private_key_id": "XXX-XXX-XXX-XXX-XXXX-XXX",
  "private_key": AN R.S.A KEY,
  "client_email": "SERVICE_ACCOUNT_NAME-PROJECT-NAME.iam.gserviceaccount.com",
  "client_id": PROJECT-CLIENT-ID,
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/SERVICE-ACCOUNT-NAME%PROJECT-NAME-PROJECT-ID.iam.gserviceaccount.com"
}

We now left with the following additional steps below in order to complete setting up our project on the Google Cloud Platform.

  1. Move the renamed file into our project directory
  2. Add the name of this file into our .gitignore file inorder to prevent it getting pushed to Github or any preferred Version Control Service.

Implementing Create User Mutation

At this point, we can begin our implementation of the createUser resolver by connecting the Google Cloud Storage using @google-cloud/storage package. Aside using this library, we have the option of interacting with the Google Cloud Storage by make direct HTTP requests to the available API Endpoints, however the Google Storage Package does that internally and more for us.

First we initiate a connection process with the Google Cloud Storage in the createUser resolver

import  { Storage } from '@google-cloud/storage';


export const Mutations = {

createUser : (_, { username, image }) => {
const bucketName = "node-graphql-application"; // our bucket name

// We pass-in the downloaded SECRET KEY from our Service Account, 
 const storage = new Storage({ keyFilename: path.join(__dirname, "../upload.json") });
  }
}

After initializing the Storage constructor import from the @google-cloud/storage package, using path we construct the file path to where the secret-key json file was stored. The is secret-key file has all the necessary data needed to authenticate with the Google Cloud.

Next, we expand our createUser resolver function to process and upload the passed in images to our Bucket on the Google Cloud Storage.

const removeWhiteSpaces = (name) => {
  return name.replace(/\s+/g, "");
};

export const Mutations = {
  createUser : async (_ , {filename , image}) => {
   const { filename, createReadStream } = await image;

    let sanitizedName = removeWhiteSpaces(filename);
    await new Promise((resolve, reject) => {
      createReadStream().pipe(
        storage
          .bucket(bucketName)
          .file(sanitizedName)
          .createWriteStream()
          .on("finish", () => {
            storage
              .bucket(bucketName)
              .file(sanitizedName)

           // make the file public
              .makePublic() 
              .then(() => {
                Data = [];

            // save user's data into the Data array
                Data.push({
                  username: username,
                  imageurl: https://storage.googleapis.com/${bucketName}/${sanitizedName},
                });
                resolve();
              })
              .catch((e) => {
                reject((e) => console.log(exec error : ${e}));
              });
          })
      );
    });
  }
}

Above we are a performing a file upload of the file passed in to the resolver function. Here is a gradual breakdown of everything being done within the resolver;

  • First, we asynchronously destructured filename and createReadStream from the uploaded file. We then rid the destructured filename of whitespaces. The Storage library will try to do this by replacing the whitespace with the percentage character ( % )and this leads to a distorted file URL which can also choose to ignore.
  • Next, we create a new promise and using Node Streams, we pipe the createReadStream to the Google Storage constructor. We resolve this promise after a successful file upload or reject it in the error promise state from the makePublic method.
  • We call the the bucket method on the storage class and pass in the name of our storage bucket and we further call the file method and pass in the name of the file and then we call the createWriteStream method to upload the file.
  • We make the file public, by calling the makePublic method after passing the bucket name and filename of the recently uploaded file.
  • We create an object of the user’s data containing the username, and a constructed url of the file uploaded to our storage bucket. The URL structure for public files on the Google Cloud Storage is https://storage.googleapis.com/{BUCKET_NAME}/{FILENAME}, using JavaScript’s template literals, we can insert our bucket name into the BUCKET_NAME placeholder and also the name of the uploaded file into the FILENAME placeholder and this would give a valid URL of the file which we can access it through.

Note: Files are private by default on the Google Cloud Storage and cannot be accessed via URL, hence the need to make the file public after uploading into our cloud bucket.

We can test the createUser endpoint using curl to perform a demo account creation.

curl localhost:4000/graphql  -F operations='{ "query": "mutation createUser($image: Upload! $username : String!) { createUser(image: $image  username : $username) { username imageuri } }", "variables": { "image": null, "username" : "Test user" } }' -F map='{ "0": ["variables.image"] }'  -F 0=test.png

In the HTTP request above, we specified the HTTP verb as a POST request and our endpoint and other request headers. After that, we specified the GraphQL operation for the createUser resolver, inferring the username and image types. Then we specified the path to the test file.

If the request above is successful, we would see the uploaded file listed in our bucket like this:

Consuming Our GraphQL API

Now we are left with building the front-end part of our application which consumes our GraphQL API. We would be bootstrapping our React application using the create-react-app cli.

To get started, run the following commands from your terminal :

# Create A New Application using Create-React-App CLI
npx create-react-app Graphql-upload-frontend

# Move into newly created project directory
cd Graphql-upload-frontend

# Dependencies needed for our application
yarn add react-dropzone @apollo/react-hooks graphql apollo-cache-inmemory

Next, we create a link to our GraphQL endpoint and initiate the Apollo Client in a separate configuration file.

// config.js

import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createUploadLink } from "apollo-upload-client";

const GRAPHQL_ENDPOINT = "http://localhost:3000/graphql"; 
const cache = new InMemoryCache()

const Link = createUploadLink({
  url: GRAPHQL_ENDPOINT,
});

export const Config = new ApolloClient({
  link: uploadLink,
  cache
})

If you have gone through the Getting Started section of the React-Apollo documentation, you would notice a slight difference in the packages used. Here is a breakdown of what we accomplished above:

  • By initializing the InMemoryCache constructor from the [apollo-cache-inmemor](https://www.npmjs.com/package/apollo-cache-inmemory)`y` package, we created a data store which stores the cache from all requests made in our application
  • We created a connection link using the apollo-upload-client package which has our single GraphQL endpoint as a value. This link handles the multi-part upload requests which is done when a file is being uploaded through a GraphQL endpoint and also handles the Query and Mutation operation.
  • We initialized the Apollo Client constructor in a variable, passed in the upload link and the cache and then exported the variable to be used by the ApolloClient provider.

We then wrap our entire application tree with the ApolloProvider, so we can make a query, mutation or subscription from any component.

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Config } from "./config";
import { ApolloProvider } from "@apollo/react-hooks";

ReactDOM.render(
    <ApolloProvider client={Config}>
      <App />
    </ApolloProvider>,
  document.getElementById("root")
);

serviceWorker.unregister();

We can above see the ApolloProvider wrap the root component and we passed in the Apollo Client which was exported from the configuration file as Config into the ApolloProvider’s client prop.

Working With GraphQL Data

At this stage, our application is almost ready to begin working with data from the GraphQL application but before that, we need to define our GraphQL operations. Remember the strong typing feature of GraphQL we previously talked about? It also applies on the Client Side.

We define our GraphQL operations using gql from the @apollo/react-hooks package. We use gql with grave accents (backticks) to parse a GraphQL string. First we define the operation type (either a mutation, subscription or query) then we give it a name. If the operation takes any arguments, we infer the types of the individual arguments in a parenthesis to a prefix identifier using a sigil operator) ($) and we can then use this typed argument through it’s prefix.

We can see a practical example of this in the three GraphQL operations we have defined below for our application.

# data.js
import { gql } from "@apollo/react-hooks";

export const CREATE_USER = gql`
  mutation createUser($username: String!, $image: Upload!) {
    createUser(username: $username, image: $image) {
      username
    }
  }
`;

export const DELETE_ACCOUNT = gql`
  mutation deleteAccount {
    deleteUser
  }
`;

export const GET_USER = gql`
  query getUser {
    getUser {
      username
      imageurl
    }
  }
`;

Above, we are defining our GraphQL operations to be used in variables and we are exporting these variables so they can be used by the application components. Here is a quick rundown of each variable:

  • CREATE_USER
    It defines the createUser mutation which receives a username of a string type and also an image which has the Upload object type from React-Apollo. The image is represents the file which is uploaded by the user with all necessary fields within.
  • DELETE_ACCOUNT
    This is also defined as a mutation, but it receives nothing hence it has no parenthesis containing any defined scalar. It only defines and name the deleteUser mutation.
  • GET_USER
    This is defined as a Query operation. We can see the two values which are returned from this query being stated in the within the curly braces. Although this query receives no argument, GraphQL queries sometime also receive arguments when fetching a specific data and the arguments are also defined in the parenthesis just like a mutation.

Now that we have a GraphQL connection in our application, we can now build out Application Layout where we make use of the previously defined GraphQL operations in two components.

Application Layout

Our application would have the following states in order to welcome a new user, create an account and lastly keep that user logged in.

  • Guest State
    This is the initial state of the application where users are shown a default username and image. A user can switch this state by creating an account.
  • Create Account State
    Users at this point can type in a username and drag ‘n’ drop or click to add an image. This is the point where the createUser mutation is fired when the submit button is clicked.
  • Signed In State
    At this point an account has been created, the image displayed is that which was uploaded by the user and is accessed using the image url from the Google Cloud Bucket.

All the states would be implemented in two components: App Component and Create Account Component. These states would be managed using React Hooks.

We begin with implementing the Guest state in the App Component, which shows a welcome text and a default stored image.

import React, { useState } from "react";

const App  = () => { 
 const [ isCreatingAccount , setCreatingAccount ] = useState(false)

 return (
  <div className="App" style={{ height: window.innerHeight - 35 }}>
      <div onClick={() => {isCreatingAccount(true)}}  className="auth" >
        <p className="auth-text">
          Sign In
        </p>
      </div>
        <div className="content"
            <img
              className="user-img"
              src={ require("./assets/groot.jpg")}
              alt="default user and user"
            />
              <h1>  Hi There, i am   Groot </h1>
              <p> You can sign-in to become you!  </p>
          </div>
    </div>
   )
}

export default App

Above we have a React component which renders; a button, an image and a default welcome text. A user can switch the application state to create an account by the click of the Sign In button.

When placed in the app.js file in our project, our application becomes similar to the application below:

We expand the App Component to switch from the default view to the input fields at the click of the Create Account button.

import React, { useState, useEffect } from "react";
import { useMutation, useLazyQuery } from "@apollo/react-hooks";
import CreateUser from "./create-user";
import "../App.css";
import { DELETE_ACCOUNT, GET_USER } from "../data";

function App() {
  const [deleteUser] = useMutation(DELETE_ACCOUNT);
  const [getUser, { data, error }] = useLazyQuery(GET_USER);

  // state used to switch between a Guest and a user
  const [isLoggedIn, setLoggedIn] = useState(false);
  const [isCreatingAccount, beginCreatingAccount] = useState(false);

  // user data stored in state and passed to GraphQL
  const [userName, setuserName] = useState("");
  const [imgUrl, setImgUrl] = useState(null);

  // deleteAccount function which deletes the user's account
  const deleteAnAccount = () => {
    deleteUser()
      .then(() => {
        // resets all stored state
        setLoggedIn(false);
        setImgUrl(null);
        setuserName("");
      })
      .catch((e) => console.log(e));
  };

  useEffect(() => {
    if (isLoggedIn && data !== undefined) {
      setImgUrl(data.getUser[0].imageurl);
    }
  }, [data]);

  return (
    <div className="App" style={{ height: window.innerHeight - 35 }}>
      <div
        onClick={() => {
          if (!isLoggedIn) {
            beginCreatingAccount(!isCreatingAccount);
          } else if (isLoggedIn) {
            deleteAnAccount();
          }
        }}
        className="auth"
      >
        <p className="auth-text">
          {!isLoggedIn ? (!isCreatingAccount ? "Sign In" : "Cancel") : "Logout"}
        </p>
      </div>
      <div className="content">
        {!isCreatingAccount ? (
          <div>
            <img
              className="user-img"
              src={imgUrl ? imgUrl : require("../assets/groot.jpg")}
              alt="default user and user"
            />
            <h1>
              Hi There, i am
              {userName.length > 3 ? ${userName} : Groot}.
            </h1>
            <p>
              {!isLoggedIn
                ? "You can sign-in to become you!"
                : "You sign-out to become Groot!"}
            </p>
          </div>
        ) : (
          <CreateUser
            updateProfile={() => {
              getUser();
              setLoggedIn(true);
              beginCreatingAccount(false);
            }}
          />
        )}
      </div>
    </div>
  );
}

export default App;

In the code above, we have made the following additions to our application;

  • We created two new states to track when the user is logged in and when the user is creating an account. These two states are updated by the Sign In button which can now start an account creation process or cancel it and return back to the default state.
  • Our application now uses the useLazyQuery hook which comes from apollo/react-hooks package to make a GraphQL query to fetch the user’s data using our previously created GET_USER definition.

    • Our query here is said to be lazy because it is not executed immediately the application is loaded. It is executed after the createUser mutation in the Create Account component has been executed successfully. According to the React - Apollo documentation, useLazyQuery does not execute it’s associated query immediately, but rather in response to events.
  • We watch the destructured data value which is undefined by default until the query is made, in a useEffect and then we switch the image src attribute to the imageurl returned from the query after querying the user’s data.

  • At the click of the Sign In button the isCreatingAccount state is updated to true and the Create Account component is shown for a user to input a username and add an image file.
  • After creating an account, a user can click the Sign Out button to invoke the deleteAUser function which executes the deleteUser mutation and when successful, it resets all state in the App Component.

Now, we can implement a drag ‘n’ drop functionality within the create-user component where an image can be dragged or clicked to open the device media explorer and after this we upload the added file to our Node server.

import React, { useState, useCallback } from "react";
import { useMutation } from "@apollo/react-hooks";
import { useDropzone } from "react-dropzone";
import "../App.css";
import { CREATE_USER, GET_USER } from "../data";

const CreateUser = (props) => {
  const { updateProfile } = props;
  const [createAccount, { loading }] = useMutation(CREATE_USER);
  // user data stored in state and passed to GraphQL
  const [userName, setuserName] = useState("");
  // user's uploaded image store in useState and passed to the GraphQL mutation
  const [userImage, setUserImage] = useState(null);

  // create user mutation function fired at the click of createAccount button
  const createAUser = () => {
    createAccount({
      variables: {
        username: userName,
        image: userImage,
      },
    })
      .then(() => {
        updateProfile();
      })
      .catch((e) => console.log(e));
  };

  const onDrop = useCallback(([file]) => {
    setUserImage(file);
  }, []);

  const {
    getRootProps,
    isDragActive,
    isDragAccept,
    getInputProps,
    isDragReject,
  } = useDropzone({
    onDrop,
    accept: "image/jpeg , image/jpg, image/png",
  });

  return (
    <div className="CreateUser" style={{ height: window.innerHeight - 35 }}>
      <div className="content">
        <div>
          <h1> {!loading ? "Create An Account" : "Creating Account ..."}</h1>
          <hr />
          <br />
          <form className="form">
            <div className="input-body">
              <label style={{ color: loading && "grey" }}> Username </label>
              <input
                disabled={loading}
                style={{ color: loading && "grey" }}
                onChange={(e) => setuserName(e.target.value)}
                placeholder="some nifty name"
                required={true}
                type="text"
              />
              <br />
              <br />
              {!userImage ? (
                <div
                  className="circle-ctn"
                  {...getRootProps({
                    isDragActive,
                    isDragAccept,
                    isDragReject,
                  })}
                >
                  <input {...getInputProps()} />
                  <div
                    className="box"
                    style={{
                      background: isDragActive && "#1b2733",
                    }}
                  >
                    <p
                      style={{ color: isDragReject && "red" }}
                      className="circle-text"
                    >
                      {!isDragActive
                        ? Tap or Drag 'n' Drop Image  to Add Profile Picture
                        : isDragReject
                        ? "Ooops upload images only"
                        : "Drop your image here to upload"}
                    </p>
                  </div>
                </div>
              ) : (
                <div className="img-illustration">
                  <img
                    style={{ filter: loading && "grayscale(80%)" }}
                    className="img-icon"
                    src={require("../assets/image-icon.png")}
                    alt="image illustration"
                  />
                  <p style={{ color: loading && "grey" }} className="file-name">
                    {userImage.path}
                  </p>
                </div>
              )}
              <br />
              <br />
              <button
                style={{
                  background: userName.length < 3 && "transparent",
                  color: userName.length < 3 && "silver",
                }}
                className="create-acct-btn"
                onClick={(e) => {
                  e.preventDefault();
                  createAUser();
                }}
                disabled={userName.length < 3 || loading}
              >
                {!loading ? "Create Account" : "Creating Account"}
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  );
};

export default CreateUser;

Here is a gradual breakdown of all that’s happening above:

  • We destructured createAccount resolver function from the useMutation hook after passing in our previously defined CREATE_USER operation.
  • We created a function;- createAUser which is invoked at the click of the Create Account button after typing in a username and adding an image.
  • We created an onDrop function which is wrapped in the useCallback to avoid a recompution of this function. After the file is dropped, we keep it temporarily in the userImage state to be used when submitting the data.
  • We destructured the four root properties from the useDropZone hook and then specified the acceptable file types alongside our custom onDrop function.
  • Next, those root properties destructured are used in building a reactive dropzone, that reacts when an acceptable file or non - acceptable file is being dragged over our dropzone. This done by applying the root properties to our selected dropzone , which here happens to be a div element wrapping other smaller div elements. Also, by spreading the …getInputProps() in the input element, it makes the input element hidden with a file type so when the dropzone is clicked, it opens the device media explorer.
  • Lastly, we used the ternary operator in the inline styles to make the div have a border when a file is being dragged over it and also make this border red when a file type not specified is being dragged.

Now at the click of the Create Account button, using a ternary operator and the loading boolean value destructured from the useMutation hook, we switch the “Create Account “ text to “Creating Account …” to indicate that the data is being submitted and a network request is in flight.

Once the mutation has been executed successfully, we execute the lazy getUser query and we switch back to the Home Component but this time with data from the getUser query. Using the imageurl value returned in the getUser query result, we can access the uploaded image over the internet and also display it in the page.

Conclusion

In this article, we have walked through three aspects of creating a file upload pipeline. First we built a frontend application where users can drag and upload a file to upload it. Then we built a GraphQL API that connects the frontend application and a mutation to handle the incoming file. Lastly we connected our server to the Google Cloud Storage to store the file from the node server.

It is also recommended to read Apollo Server File Upload Best Practices on two more ways of performing file in a GraphQL application.

All files and code snippets referenced and used within this article is available Github.

References

How to build an app like Uber?

I've seen so many entrepreneurs trying to build an app like Uber with Uber like solutions. There are so many Uber clone solutions out there such as Wooberly to help them out. But how to build it from scratch?

Seriously, OOP sux!

I have decided I will commit several murders. My first victim will be OOP, or to be specific; Your assumptions about OOP. However, don't believe me, let's ask people smarter than me, right? Some wise guy once said "You want the banana, you get the gorilla, holding a banana, in the rainforest". I can't remember who it was, but he was talking about reuse in regards to OOP. I've got 20+ years of professional experience in OO, I think I'm still quoted in regards to virtual copy constructors in a couple of the C++ drafts, and I've got almost 3 digits numbers of emails back and forth with Bjarne Stroustrup in regards to the std - Hence, I (should) know what I'm talking about, and believe me, OOP seriously sux! If you're a noob programmer, trying to grasp polymorphism, constructors, inheritance, SOLID and encapsulation - Don't worry, it's not you - It's OOP!

But don't believe me, let's go ask 1.000 random software developers who's the best software developer on this planet. 999 of those will probably answer Thorvalds Linus. So let's ask Thorvalds what he thinks about OOP. He should know, right ...?