Building Recruitment Management App using Appwrite and ToolJet

This tutorial will guide you to easily build a recruitment management application in less than 30 minutes using ToolJet's low-code application builder.
The recruitment management app will allow you to manage your organization's recruiting and staffing operations. Use this application for keeping your applicants connected and engaged throughout your entire recruiting process.
Here are some of the main features of this application:
- Applicants tracking options like approving or rejecting applicants.
- Engage with applicants or schedule interviews using email.
- Limit features like adding applicants depending on the current user or user group type.
To build this application we will use:
- ToolJet (https://github.com/ToolJet/ToolJet): A free and open-source low-code platform that allows you to quickly build applications. Sign up here.
- Appwrite: Open-Source backend server for Web, Mobile & Flutter developers. For this application, I have deployed Appwrite on DigitalOcean using the Appwrite droplet.
- SMTP: SMTP server for sending emails to applicants. Read the docs for the SMTP data source here.
The tutorial is broken down into the following sections:
- Setting up Appwrite
- Creating collection and documents in Appwrite
- Adding Appwrite data source on ToolJet
- Building the app UI
- Creating queries
- Editing widget properties and connecting queries
- Enabling app features for particular User Group
Setting up Appwrite
We'll be deploying Appwrite on DigitalOcean. To do the this, follow the steps:
- Sign up for a free account on DigitalOcean, it may ask for card details for verification purposes while signing up for the first time.
- Once signed up, you'll receive free $100 credits valid for 60 days from the date of registration. You can use these credits for deploying apps.
- Now create an Appwrite droplet in your DigitalOcean account from the following link: https://marketplace.digitalocean.com/apps/appwrite
4. Next, select the capabilities that you want for your Droplet. We will go with the “Basic” option of a shared CPU and at least 1 GB of RAM. Select the cheapest option with Intel CPU with SSD which is for $5 per month.
5. In the next step, create the login credentials for your virtual machine. You can either add SSH keys or create a root password. Adding SSH keys is the recommended way but if you're new you can go with the root password.
6. Click on finalize and create, after some time DigitalOcean will have your virtual machine running.
7. Finally, you can go to the IP address or URL for our new server and log in to the Appwrite console.

Creating collections and documents in Appwrite
- Once you have signed in to the Appwrite console, create a project. Let's call this project:
recruitment-management

2. Go to the Database from the sidebar in the console, and click on the Add Collection
button. In the modal, enter the collection name as Applicants
and click Add button.

3. In Appwrite, Collections hold Documents. We will add a few sample Documents to the Collection that we created. You can think of Collections as tables and Documents as rows. But before adding documents, we will need to add Attributes.
4. We will need the following attributes for the documents in our collection:
- Attribute ID(key): name, Type: String (Name of the applicant)
- Attribute ID(key): birthdate, Type: String (Birthdate of the applicant. Ex: DD-MM-YYYY)
- Attribute ID(key): city, Type: String (For the  city of the applicant)
- Attribute ID(key): phone, Type: integer (Phone number of the applicant. Ex: 1234567890)
- Attribute ID(key): email, Type: string(email) (Email of the applicant. Ex: abc@xyz.com)
- Attribute ID(key): resume, Type: string(URL) (URL of the resume of the applicant. Ex: https://drive.google.com/xxxxx)
- Attribute ID(key): position, Type: String (Position for which the applicant applied. Ex: Frontend Developer)
- Attribute ID(key): picture, Type: string(URL) (URL of the picture of the applicant. Ex: https://www.imgr.com/xxxx)
- Attribute ID(key): approved, Type: String (Status of the Applicant. This will include any one of these values - new, yes, or no)

5. Finally, you can start adding documents to the collection.
new
as the value for the approved key. We will use new
for new applicants and later update the value for approved as yes
or no
.Adding Appwrite data source on ToolJet
I am assuming that you have already created an account on ToolJet and logged in to the dashboard. Now click on Create new application button and then give your first version app a name then click Create.
Before we start with setting up the Appwrite data source, let me give you a quick walkthrough of the ToolJet App Editor:

Now let's look at the steps for adding the Appwrite data source:
- Go to the left sidebar, and click on the Datasource.
- In the modal that pops up, select the Appwrite data source.
- Now you'll be asked to enter the - Host, Project ID, and Secret Key. You'll find these three in your Appwrite console.
- Now click on the Test Connection button to verify that the Appwrite server is connected successfully.
5. Click on Save to add the data source.
Building the application UI
Now that we have connected the data source, the next steps are to build the user interface, create the queries for performing several operations, and then connect the UI and queries to make our app functional.
For building the application UI, we will use the following widgets:
- Containers
- Circular progress bars
- Modals
- List view
- Buttons
Here's how your application will look in the end:

Let's start building the UI:
- Drag and drop a Text widget to canvas and click on it to open the widget properties inspector panel on the right sidebar. We will use the HTML heading tags <h1> to style our text. Enter <h1>Recruitment Management Dashboard</h1> in the Text field.
- Now drag a container, and place it below the Title of the app. We will use this container to display approved applicants' stats using a circular progress bar and text. Follow the steps to build the UI:
- Click on the container's widget handle to open the properties. In the Styles tab, Add
#667ACD
as the hex color code for the background and set the Border radius to10
. - Drag a text widget inside the container, add the text as
Summary of approved
and set the text color to#FFFFFF
- Drag two more text widgets that will be used to display the approved number of applicants from the query and the total applicants. We haven't yet created the queries so for now let's add dummy text to these two widgets and place them. For the first text widget add
1/
and second text widget2
. - Now drag the circular progress bar inside the container and click on the widget handle to edit its properties. Go to Styles and change the color to
#F39200
, we'll edit the remaining properties later once we have created the queries.
- Click on the container's widget handle to open the properties. In the Styles tab, Add

- Now we will duplicate the component that we created in the previous step. This will be used to display the number of rejected applications. To duplicate, click on the handle of the container and press
cmnd/ctrl+d
. You'll see a duplicate component overlapping the first one. Drag the duplicate container and place it next to the first one. - Now, make changes in the text and color of the duplicate container. It will include duplicate widgets of all the widgets that are there in the first container.

- Now drag a button next to the container of
Summary of rejected applicants
. We will use this button to trigger a modal - that will have a form for adding new applicants. So, we will add the textAdd applicant
on this button. For Styling, set the Border radius10
and color to#F39200
. - Let's duplicate the button and rename its text to
Refresh
. This button will be used to refresh the list of applicants.

- Now we will need to build a sidebar on the right to show the selected applicant basic details that will include some buttons for performing actions like checking resume, sending email, and approving or rejecting the selected applicant. To build the left sidebar, follow the steps:
- Drag a container on the right of the buttons and adjust its height to reach the bottom of the canvas.
- Let's start putting the widgets inside the container. We'll start by putting the image widget on the top of the container. It will be used for displaying the selected applicants' pictures. Go to the Style of image widget and choose border type as the circle.
- Now drag a text widget below the image widget, edit its properties and enter the dummy text for now. We'll get the value from the selected applicant once we are done creating queries.
- Now we'll need to drag a few more text widgets and fill the dummy text values for displaying the Basic Information such as Birthdate, City, Phone Number, and Email. (Check the next screenshot)
- Now add a button below the basic information that will be used to open the resume. Edit the properties of the button - Fill the text value as
Resume
. Go to the Style, set the background color to#D4DAF1
, text color to#000000
, and border radius to20
. - We'll duplicate the button created in the previous step using
cmnd/ctrl+d
. Edit the text on the button toSend Email
. This button will be used to show the modal that will include the fields for sending emails to the selected applicants. - Now add a new button at the bottom-left for Accepting the selected applicant. Let's resize the button to make it a square button. Set the button text to
Approve
. Go to the Style, set the background color to#909EDB
and border radius to20
. - Duplicate the button for accepting the applicants, change the text to
Reject
. Go to Style, update the color to#DD7179
.

We are almost done with 50% of the user interface. All we need to do now is to build the UI for displaying the New Applicants list, Accepted Applicants list, Rejected Applicants list, Add Applicants modal, and Send Email modal.
Let's build the UI for the New Applicants list:
- Drag a Container in the center and scale it to occupy the remaining blank space. Set the border radius of the container to
10
. - Drag a Text to top-left of the container and set the Text field value to <h2> New Applicants </h2 >
- Drag a new container within this container and scale it horizontally from the left to right of the parent container. Use three text widgets within this container and change the text to
Name
,Email
,Resume
, andPosition
respectively. Check the screenshot below.
- Now, drag a List view widget below the child container that we added above. We'll add a few widgets inside the first row of the List view widget. Set the Row height of the List view to
80
. - Add the Image widget below the
Name
, set the URL of the Image widget to{{listItem.picture}}
and border type tocircle
. Read List view docs to learn more about showing data. - Add a Text widget next to the image and set the text field to
{{listItem.name}}
- Similarly a text widget under Email and Position and set the value to
{{listItem.email}}
and{{listItem.position}}
respectively. - For the Resume, we will add a Button and add an Event handler to the button. Set Event to On click and choose action Open webpage. Set the webpage URL to
{{listItem.resume}}
.
The New applicant section should look similar to the screenshot but with just one row in the list because we haven't yet added the data to the list. We'll add the data to the List view after we are done creating the queries.
Now let's duplicate the container for the New Applicants twice to create two new lists for Accepted and Rejected. See the screenshot below:

Finally, let's add a Modal for Add Applicants button. Drag a modal at the blank space below the lists. Now, go to the Add Applicants button and add a handler for the On Click event and Show Modal action, and select the modal1 in the dropdown.

Now when you'll click on the Add Applicant button the modal will show up. You can drag and drop widgets from the widgets manager in the left sidebar and create a form like this:

Let's add another Modal for Send Email button, add an event handler in the Send Email button the same way we did for Add Applicant button but choose Modal2 in the dropdown.
Click on the Send Email button to show up the modal and then add the widgets to build the form for sending email as shown in the screenshot. Use Text, Text Input, Text Area, and Button widgets to create this form:

Finally, we are done with building the User Interface of the application. Now the next step is to create queries to perform operations on the Appwrite database and then connect the queries and user interface.
Create the queries
To make our application fully functional we'll be creating these queries:
- listApplicants (Appwrite)
- approvedApplicants (Appwrite)
- rejectedApplicants (Appwrite)
- addApplicant (Appwrite)
- moveToApprove (Appwrite)
- moveToReject (Appwrite)
- sendEmail (SMTP)
- totalApplicants (Custom JavaScript)
1. Â listApplicants
This query will be used for listing the new applicants. Let's create a new query by clicking on the +
in the query panel.
- Select the Appwrite data source and choose the operation List Documents.
- Enter the Collection ID, you can get it from the Appwrite console.
- We will filter the query results using Field, Operator, and Value options. We want the query to list only new applicants so we will enter approved in Field, == in Operator, and new in Value. You'll need to create an index in the Appwrite console for approved key.
- Go to the Advanced tab and enable the toggle for Run this query on page load? This will fire the query every time the app is loaded.
- Rename this query to
listApplicants
and click Save. - You can also click on the Preview button to check the results without firing up the query.

2. approvedApplicants
This query will result the list of only those applicants that have yes
value in approved column/key.
- Create a new query, select Appwrite data source, and select the List Documents operation from the operations dropdown.
- Enter the Collection ID
- In the Field, Operator, and Value fields enter approved, ==, and yes respectively. (make sure you have added an index for the approved key)

- Rename this query to
approvedApplicants
and click Save to create the query. - Now go to the Advanced tab of listApplicants query, add an event handler to run the query
approvedApplicants
on Query Success event.

3. rejectedApplicants
This query will result the list of only those applicants that have no
value in approved column/key.
- Create a new query, select Appwrite data source, and select the List Documents operation from the operations dropdown.
- Enter the Collection ID
- In the Field, Operator, and Value fields enter approved, ==, and no respectively. (make sure you have added an index for the approved key)

- Rename this query to
rejectedApplicants
and click Save to create the query. - Now go to the Advanced tab of approvedApplicants query, add an event handler to run the query
rejecteedApplicants
on Query Success event.

4. addApplicant
This query will be used for adding a new applicant.
- Create a new query, select Appwrite data source, and select the add a document to the collection operation from the operations dropdown.
- Enter the Collection ID, you can get it from the Appwrite console.
- In the Body field, we'll get the values for the new document in JSON format:
{
"name": "{{components.textinput3.value}}",
"birthdate": "{{components.textinput4.value}}",
"city": "{{components.textinput5.value}}",
"phone": {{components.textinput6.value}},
"email": "{{components.textinput7.value}}",
"resume": "{{components.textinput2.value}}",
"position": "{{components.textinput8.value}}",
"approved": "new",
"picture": "{{components.textinput9.value}}"
}

- Go to the Advanced tab, and add three event handlers. 1st one to close the Modal1 on Query Success event, 2nd to show alert
Applicant Added
with alert type as Success on Query Success event, and last one to run the querylistApplicants
on Query Success event - this will refresh the list and include the recently added applicant.

- Rename this query to
addApplicant
and click Save to create the query.
5. moveToApprove
This query will move the selected applicant from New Applicants list to the Approved Applicants list. This query will be triggered from the Approve button in the sidebar.
- Create a new query, select Appwrite data source, and select the Update Document operation from the operations dropdown.
- Enter the Collection ID
- In the Document ID, we will enter
{{queries.listApplicants.data.documents[components.listview1.selectedRowId].$id}}
. This is getting the Document ID of the selected applicant from the list. - In the body field, we'll enter the updated document as JSON object.
{
"name": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].name}}",
"birthdate": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].birthdate}}",
"city": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].city}}",
"phone": {{queries.listApplicants.data.documents[components.listview1.selectedRowId].phone}},
"email": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].email}}",
"resume": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].resume}}",
"position": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].position}}",
"approved": "yes",
"picture": "{{queries.listApplicants.data.documents[components.listview1.selectedRowId].picture}}"
}
So basically we are updating just one key in the document that is the approved key - updating its value from new
to yes
.

Now go to the Advanced tab and add two handlers - One to show the alert Applicant Approved
on query success and the other to run the query listApplicants
on query success.

- Rename this query to
moveToAccept
and click Save to create the query.
6. moveToReject
This query will move the selected applicant from New Applicants list to the Rejected Applicants list. This query will be triggered from the Reject button in the sidebar. Under the hood, we will be just updating the value for approve key from new
to no
.
There is no difference in this query from moveToApprove
query - except in that query we were updating the value of approve key from new
to yes
and in this one will be updating it to no
.

- Go to the Advanced tab, and add two handlers - One to show the alert
Applicant Rejected
on query success and the other to run the querylistApplicants
on query success.

- Rename this query to
moveToReject
and click Save to create the query.
7. sendEmail
This query gets the values from Modal2 which is shown when Send Email button is clicked. Before creating this query, you'll need to add the SMTP data source. Check the docs for adding SMTP data source.
- Create a new query, and select SMTP data source.
- Enter the email in From field, and sender's name in From Name field.
- In the To field, we'll get the value from the selected applicant in the list. We'll use the exposed variable selectedRow to get the value -
{{queries.listApplicants.data.documents[components.listview1.selectedRowId].email}}
- In the Subject field, we'll get the value from the text input component used inside the modal2 -
{{components.textinput1.value}}
, similarly in the Body field -{{components.textarea1.value}}
- Go to the Advanced tab, and create two event handlers - one to close the modal2 and the other to show alert
Email sent

8. totalApplicants
This query will result the total number of Applicants i.e. New+ Approved+ Rejected. To do this, we'll create a new Custom JS query and write a simple JavaScript code:
var total = parseInt(queries.listApplicants.data.total + queries.approvedApplicants.data.total + queries.rejectedApplicants.data.total);
return total;
We'll use the result from this query on the Summary of approved and Summary of rejected containers to show the number of total applicants.
Editing widget properties and connecting queries
Now that we have built the user interface and created all the queries, all we need to do is connect the UI with queries. Let's get started:
- Go to the Summary of approved section, select the text widget that has
2/
value, and replace the value with{{queries.approvedApplicants.data.total}}/
. In the loading state field, enter{{queries.approvedApplicants.isLoading}}
. - In the text widget that has a dummy value
9
replace it with{{queries.totalApplicants.data}}
. In the loading state field, enter{{queries.totalApplicants.isLoading}}
. - In the Summary of rejected section, enter
{{queries.rejectedApplicants.data.total}}
to show rejected applicants in the first text widget, and set its loading state to{{queries.rejectedApplicants.isLoading}}
. - In the text widget that has total applicants enter
{{queries.totalApplicants.data}}
. In the loading state field, enter{{queries.totalApplicants.isLoading}}
. - Click on the widget handle of Add Applicant button to open its properties. Set its loading state to
{{queries.addApplicant.isLoading}}
, and an event handler to show the modal1 for the On Click event. - Click on the widget handle of the Refresh button, set the Loading state to
{{queries.listApplicants.isLoading}}
and add a handler to run listApplicants query on the On Click event. - Now, click on the Image in the right sidebar that is built using the container. Enter the URL for the image
{{queries.listApplicants.data.documents[components.listview1.selectedRowId].picture}}
- We'll do the same for basic details (get the data using the selectedRowID variable), for Birthdate enter text value as
{{queries.listApplicants.data.documents[components.listview1.selectedRowId].birthdate}}
, for the city enter{{queries.listApplicants.data.documents[components.listview1.selectedRowId].city}}
, for Phone number enter{{queries.listApplicants.data.documents[components.listview1.selectedRowId].phone}}
, and for email enter{{queries.listApplicants.data.documents[components.listview1.selectedRowId].email}}
- Open properties of the Resume button and add the handler to Open a webpage on On Click. Set the URL to
{{queries.listApplicants.data.documents[components.listview1.selectedRowId].resume}}
- Click on the handle of Send Email button to edit its properties. Add a handler to show the Modal2.
- Click on the Approve button handle, set the loading state to
{{queries.moveToApprove.isLoading}}
, and add a handler to run the moveToApprove query. - Click on the Reject button handle, set the loading state to
{{queries.moveToReject.isLoading}}
, and add a handler to run the moveToReject query. - Now select the List view widget inside the container of New Applicants. In the List data field enter
{{queries.listApplicants.data.documents}}
. After entering this you'll see that the list now has all the applicants. - We'll do the same for the other two list view widgets - Approved and Rejected lists. For Approved List data enter
{{queries.approvedApplicants.data.documents}}
, and for Rejected List data enter{{queries.rejectedApplicants.data.documents}}
We have successfully connected the queries and UI and our app is fully functional now.
Enabling app features for particular User Group
Now that we have successfully built the app, we want some of our features like Adding Applicants only available to a certain group of app users. Let's assume you have two types of users who will use this app:
- group1 - users who will be authorized to create applicants
- group2 - Users who will not be authorized to create applicants but can use other features of the app
To achieve this functionality you'll need to create two groups in your organization and add those users in respective groups. Check out the docs for managing users and groups here.
Let's disable the Add Applicant button for all the users that are not in the group1:
- Click on Add Applicant button handle to edit its properties.
- Go to Style, click on Fx next to Disable, and enter
{{globals.currentUser.groups[1] !== "group1" ? true : false}}
- Now whenever a user who is not in the group1 will use the app the Add Applicant button will be disabled.
Finally, make the application live by clicking on the Release button at the top-right corner of the app editor.
Congratulations! 🥳  You’ve successfully built the Recruitment management app. If you have any questions feel free to join our Slack community or send us an email at hello@tooljet.com.