Server Messaging App

Creating a Messaging Server in Low-Code

May 20, 2022

[Guide]

Implementing the Server-side of a Client-server Style Messaging App

We will implement some web methods, which will be useful for creating a messaging client. However, the following items fall outside the scope of this sample:

  • The messaging client. We will use Postman to simulate a client, to test our web methods
  • Security, is an essential aspect of any real system
  • Data administration
  • Encryption
  • Automated regression testing. We will do this in a subsequent sample

 

Pre-requisites

 

The Web API

For our web API to be useful, it should allow us to do the following:

  • Add a user
  • Get a list of users
  • Send a message to a user
  • Get a list of messages for a user
  • Read a message

We will add a method for each of these to our web API.

 

Business Rules

A few rules will help our messaging app implementation make more sense:

  • Each user will be uniquely defined by his/her email address. No two users may have the same email address. We call this the natural key for the user. Each user will also have a surrogate key in the database. In MongoDB, the surrogate key field is a special field named _id. The reasons why this is a good practice are beyond the scope of this sample. It might be helpful to Google it at some point if you don’t know.
  • Each message is for one and only one user. The message must therefore contain a field for the associated user’s _id. A message has a surrogate key as for a user, but there is no logical natural key for a message.

 

Conventions

  • We will use underscores in names in Linx, wherever we would otherwise want to use spaces because Linx does not play nicely with spaces in names.
  • The property values for some functions in Linx may contain expressions. In the value input box of such a property, an expression is denoted with a prepended “=” (equals sign). Throughout this sample, expressions like these will be provided with the = prefix included, for ease of pasting directly into the property value input box. If such an expression is inserted into the expression editor, the = prefix should be omitted.

 

 

Create the Linx Solution for the Messaging App

Open the Linx designer, and create a new project. Name the solution Messaging_App, and name the default project Server. Delete the default Process from the Server project.

 

Messaging App Dependencies

We will need the following Linx plugins (from the toolbox on the right) to implement our messaging server:

  • RESTfor the web service which will host our web API.
  • Database, for the MongoDB tools for reading and writing data.

 

Messaging App Settings

In the course of implementing our messaging app, we will refer repeatedly to things like the database connection, and the base URL of our web API server. What’s more, these values would have different values in the different deployment sandboxes of a real system. Some other properties of our system, for example, whether our web API should display server errors or not, would also have different values in the different deployment sandboxes of a real system. Linx provides solution configuration Settings, to manage settings like these.

Add the following settings:

 Name  Type Value
 Database_connection String mongodb://localhost/Messaging_App
 Server_base_URL  String http://localhost:8095/messaging_app/api
 Show_server_errors  Boolean True
Note that the base part of your database connection may differ, depending on the setup of your environment. Connect to your database with a tool like Robo 3T to establish the correct format.

The server base URL above assumes that port 8095 is available in your environment. If not, use another port that is available. It also assumes that the localhost hostname is configured in your environment. If not, you may have to use an IP address instead. For example 127.0.0.1 for your local machine, or use the ipconfig command to determine your local network IP address.

 

Web API Data

In order to move user and message data in and out of the web API methods and the database, we need to define exactly what user and message data looks like. Add a new folder to the Server project, and name it Data.   Add a new custom type to the Data folder, and name it User. Add the following properties to the User type:

 Name  Type
_id String
Email  String
 First_name  String
Last_name String
Add a new custom type to the Data folder, and name it Message. Add the following properties to the Message type:
 
 Name  Type
_id String
 User_ID  String
Text String
Sent DateTime
 Is_read  Boolean
 

 

Define a Web API in Linx

Expand the REST plugin in the Plugin toolbox on the right. Add a SimpleRESTHost to the Server project, and set the following properties on it:

 Property Value
 Name Web_service
 Base URI =$.Settings.Server_base_URL
 Show server errors =$.Settings.Show_server_errors

Edit the Web_service Operations property, and add the following operations:

Name Path Method Request Body

Response body

Add_user

/add_user

Post

Server.Data.User

None

Get_users

/get_users

Get

None

List<Server.Data.User>

Send_message

/send_message

Post

Server.Data.Message

None

Get_messages_for_user

/get_messages_for_user/{User_ID}

Get

None

List<Server.Data.Message>

Read_message

/read_message/{Message_ID}

Get

None

Server.Data.Message

 

Edit the Query string parameters property of the Get_messages_for_user operation, and add the following query string parameter:

Name Type

Is_read

String

Save the newly added query string parameter, and save the operations. Operation handlers are added to Web_service for each of the defined operations, in the Solution Explorer.

 

Implement the Add_User Web Method

To implement Add_user, we first check if the specified user’s email address already exists in our database, since we have a rule that duplicate email addresses are not permitted. If so, we return an error to indicate this. Otherwise, we persist with the specified user.

Select the Add_user handler. Add a MongoDBRead from the Plugin toolbox to the Add_user designer, and set the following properties:

Name Type

Name

Retrieve_user_by_email

Connection string

=$.Settings.Database_connection

Collection

User

Operation

Find

Query

{Email:”@{$.Input.Data.body.Email}”}

Output type

Server.Data.User

Return options

First row, else empty row

 

Add an IfElse from the toolbox, after Retrieve_user_by_email, and name it If_email_exists. Edit its Conditions property and add one condition named True, with the value:

=Retrieve_user_by_email != $.System.Null

Add a ThrowException from the toolbox to the True path of If_email_exists, and name it Throw_error. Set its Message property to:

="A user with email address " + $.Input.Data.body.Email + " already exists."

Add a MongoDBWrite after If_email_exists, and set the following properties:

Property

Value

Name

Persist_new_user

Connection string

=$.Settings.Database_connection

Collection

User

Operation

Insert

Data

=$.Input.Data.body

 

Test the Get_Users Web Method

Now let’s test Add_user. Launch Postman, and add a new collection named Messaging App. Manage environments, at the top right, and add an environment, also named Messaging App. Add a new variable to it named Server_base_URL, and set its INITIAL VALUE to the web service base URL, in our case http://localhost:8095/messaging_app/api. Since the path of Add_user is /add_user, we will call it at {{Server_base_URL}}/add_user.

Add a request to the Messaging App collection, with the following information:

Name

Add_user

HTTP method

POST

URL

{{Server_base_URL}}/add_user

Body format

raw

Body content type

JSON

Body text

{

    “First_name” : “Joe”,

    “Surname” : “Bloggs”,

    “Email” : “joe.bloggs@example.com”

}

 

Note that the request body text is a JSON format instance of the User type, which we set as the request body type in the definition of our Add_user operation.  In the Linx Designer, select the Web_service in the Solution Explorer, and DEBUG it from the toolbar. Once the debugger is ready, START it from the toolbar. Once the web service has started, our web API is listening at http://localhost:8095/messaging_app/api.

Send the Add_user request in Postman. If the request succeeds, the HTTP response code should be 200 OK. Launch Robo 3T and connect to your database server. The newly created user should appear in the Messaging_App database’s User collection.  Send the Add_user request again. The HTTP response code should be 500 Internal Server Error, with an error that indicates that the specified email address already exists in the system. Add another user with the following request body:

 STOP and CLOSE the debugger in the Linx Designer.

Implement the Get_Users Web Method

Select the Get_users handler. Add a MongoDBRead to the Get_users designer, and set the following properties:

Property

Value

Name

Retrieve_users

Connection string

=$.Settings.Database_connection

Collection

User

Operation

Find

Output type

Server.Data.User

Return options

List of rows

Next, add a SetValue from the standard Linx functions in the Plugin toolbar, and set the following properties:

Property

Value

Name

Set_response

Target

=$.Output.Data

 

Edit the Source property, and set the ResponseBody property to=Retrieve_users.

 

Test the Get_Users Web Method

In Postman, add another request to the Messaging App collection, with the following information:

Name

Get_users

HTTP method

GET

URL

{{Server_base_URL}}/get_users

In the Linx Designer, select the Web_service and start the debugger again. Send the Get_users request in Postman. The current list of users should be returned in JSON format.  Stop and close the debugger in the Linx Designer.

 

Implement the Send_Message Web Method

To implement Send_message, we first check if the specified user exists in our database since we have a rule that a message must be for a user. If not, we return an error to indicate this. Otherwise, we stamp the Sent time on the message and then persist it.  Select the Send_message handler. Add a MongoDBRead to the Send_message designer, and set the following properties:

Property

Value

Name

Retrieve_user_by_ID

Connection string

=$.Settings.Database_connection

Collection

User

Operation

Find

Query

{_id: ObjectId(“@{$.Input.Data.body.User_ID}”)}

Output type

Server.Data.User

Return options

First row, else empty row

Add an IfElse, and name it If_user_found. Edit its Conditions property and add one condition named False, with the value: =Retrieve_user_by_ID == $.System.Null

Add a ThrowException to the False path of If_user_found, and name it Throw_error. Set its Message property to: =”User with ID ” + $.Input.Data.body.User_ID + ” not found.”  Add an instance of the Message data type from the Solution Explorer, after If_user_found, and set the following properties:

Property

Value

Name

new_message

Value

=$.Input.Data.body

Add a SetValue, and set the following properties:

Property

Value

Name

Set_new_message_sent_time

Target

=new_message.Sent

Source

=$.System.CurrentDateTime

Add a MongoDBWrite, and set the following properties:

Property

Value

Name

Persist_new_message

Connection string

=$.Settings.Database_connection

Collection

Message

Operation

Insert

Data

=new_message

 

Test the Send_Message Web Method

In Postman, add a request with the following information:

Name

Send_message

HTTP method

POST

URL

{{Server_base_URL}}/send_message

Body format

raw

Body content type

JSON

Body text

{

    “User_ID”: “5faa6094bc6e2d10a89fcbaf”,

    “Text”: “Hello world.”

}

 As before, the request body text is a JSON format instance of the Message type, which we set as the request body type in the definition of our Send_message operation.  In the Linx Designer, select the Web_service and start the debugger again. Send the Send_message request in Postman. This request should fail with HTTP status code 500, and an error to indicate that the user with the specified ID is not found. This is because the User_ID in the Send_message request body sample, highlighted in red above, is from my database. You will have to change this to use the correct value from your database.

In Robo 3T, edit the document of the first user in the User collection. Copy the value of the ObjectId, and replace the User_ID in the Send_message request body in Postman.  Send the Send_message request again. This time the HTTP response code should be 200 OK. Launch Robo 3T and connect to your database server. The newly sent message should appear in the Message collection.  Send the Send_message request once more. This should succeed because there is no natural key violation.  Stop and close the debugger in the Linx Designer.

 

Implement the Get_Messages_for_User Web Method

To implement Get_messages_for_user, we first check that a value has been provided for the user ID since this is a required parameter. If not, we return an error to indicate this. Otherwise, we check if a value has been provided for the Is_read parameter, which is optional. If so, we format a query filter for the Is_read field of the message collection in the database. Then we retrieve the messages in descending sent time order and return them.

Select the Get_messages_for_user handler. Add an IfElse to the Get_messages_for_user designer, and name it If_user_ID_is_empty. Edit its Conditions property, and add a condition named True, with the value: =$.Input.Data.User_ID == $.System.Null || $.Input.Data.User_ID.Trim() == “”

Add a ThrowException to the True path of If_user_ID_is_empty, and set the following properties:

Property

Value

Name

Throw_error

Message

User_ID is required.

Add a String variable from the standard Linx plugins, after If_user_ID_is_empty, and name it is_read_query_filter. Add an IfElse, and name it If_is_read_parameter_has_value. Edit its Conditions property, and add a condition named True, with the value:

=$.Input.Data.Is_read != $.System.Null && ($.Input.Data.Is_read.Trim().ToLower() == “false” || $.Input.Data.Is_read.Trim().ToLower() == “true”)

Add a SetValue to the True path of If_is_read_parameter_has_value, and set the following properties:

Property

Value

Name

Set_is_read_query_filter

Target

=is_read_query_filter

Source

=”, \”Is_read\” : ” + $.Input.Data.Is_read

Add a MongoDBRead after If_is_read_parameter_has_value, and set the following properties:

Property

Value

Name

Retrieve_messages_by_user_ID

Connection string

=$.Settings.Database_connection

Collection

Message

Operation

Find

Query

{

    User_ID:”@{$.Input.Data.User_ID}”

    @{is_read_query_filter}

}

Sort

{ Sent: -1 }

Output type

Server.Data.Message

Return options

List of rows

Add a SetValue, and set the following properties:

Property

Value

Name

Set_response

Target

=$.Output.Data

Edit the Source property, and set the ResponseBody property to =Retrieve_messages_by_user_ID

 

Test the Get_Messages_for_User Web Method

In Postman, add another request with the following information:

Name

Get_messages_for_user

HTTP method

GET

URL

{{Server_base_URL}}/get_messages_for_user/5faa6094bc6e2d10a89fcbaf?Is_read=false

In the request Params of the Get_messages_for_user request, add a parameter with KEY Is_read and VALUE false.  In the Linx Designer, select the Web_service and start the debugger.

Send the Get_messages_for_user request in Postman. This request should succeed but will return no messages. This is because the user ID in the Get_messages_for_user request URL sample, highlighted in red above, is from my database. You will have to change this to use the correct value from your database.

In Robo 3T, edit the document of the user in the User collection, to which we have sent messages previously. Copy the value of the ObjectId, and replace the user ID in the Get_messages_for_user request URL in Postman. Now send the Get_messages_for_user request again. The current list of messages for the specified user should be returned in JSON format.  Stop and close the debugger in the Linx Designer.

 

Implement the Read_Message Web Method

To implement Read_message, we try to retrieve the message by the provided message ID. If the message is not found, we return an error to indicate this. This will cover the case where the Message_ID, which is a required parameter, has not been provided. Otherwise, we check if the message has been read before. If not, we mark it as read. Then we return the message.

Select the Read_message handler. Add a MongoDBRead to the Read_message designer, and set the following properties:

Property

Value

Name

Retrieve_message_by_ID

Connection string

=$.Settings.Database_connection

Collection

Message

Operation

Find

Query

{_id: ObjectId(“@{$.Input.Data.Message_ID}”)}

Output type

Server.Data.Message

Return options

First row, else empty row

 
 Add an IfElse, and name it If_message_found. Edit its Conditions property, and add a condition named False, with the value: =Retrieve_message_by_ID == $.System.Null


Add a ThrowException to the False path of If_message_found, and set the following properties:

Name Type

Is_read

String

Add another IfElse after If_message_found, and name it If_message_is_read. Edit its Conditions property, and add a condition named False, with the value: =Retrieve_message_by_ID.Is_read == false

 

Property

Value

Name

Throw_error

Message

=”Message with ID ” + $.Input.Data.Message_ID + ” not found.”

Add an instance of a message data type from the Solution Explorer, to the False path of If_message_is_read, and set the following properties:

Add a SetValue to the False path of If_message_is_read, and set the following properties:

Property

Value

Name

Mark_message_as_read

Target

=read_message.Is_read

Source

True

Add a MongoDBWrite to the False path of If_message_is_read, and set the following properties:

Property

Value

Name

Persist_read_message

Connection string

=$.Settings.Database_connection

Collection

Message

Operation

Replace

Data

=read_message

Add a SetValue after If_message_is_read, and set the following properties:

Property

Value

Name

Set_response

Target

=$.Output.Data

 

 

Edit the Source property, and set the ResponseBody property to =Retrieve_message_by_ID

 

Test the Read_Message Web Method

In Postman, add another request with the following information:

Name

Read_message

HTTP method

GET

URL

{{Server_base_URL}}/read_message/5faa7e92bc6e2d10a89fd093

 

In the Linx Designer, select the Web_service and start the debugger.  Send the Read_message request in Postman. This request should fail with an error indicating that a message with the specified ID is not found. This is because the message ID in the Read_message request URL sample, highlighted in red above, is from my database. You will have to change this to use the correct value from your database.

 

In Robo 3T, edit the document of the first message in the Message collection. Copy the value of the ObjectId, and replace the message ID in the Read_message request URL in Postman.

Now send the Read_message request again. The message with the specified ID should be returned in JSON format.

Stop and close the debugger in the Linx Designer.

Voila! You’re done.

 

For more samples, visit our Github repo 

This guide was originally posted on Dzone