Skip to main content

Browse API - Javascript Client

The ASP.NET Core SignalR JavaScript client library enables developers to call server-side hub code.
This tutorial teaches the basics of building a Javascript client using SignalR.

First we'll do the client setup and then we will show an example how to invoke (or call) the GetVouchers() method from our API.
The result array from the response can be used to implement browsing functionality.

Client setup

A JavaScript client requires references to jQuery and the SignalR core JavaScript file.
The jQuery version must be 1.6.4 or major later versions, such as 1.7.2, 1.8.2, or 1.9.1.

Install the SignalR client package

The SignalR JavaScript client library is delivered as an npm package. The following sections outline different ways to install the client library.

Install with npm

For Visual Studio, run the following commands from Package Manager Console while in the root folder. For Visual Studio Code, run the following commands from the Integrated Terminal.

npm init -y
npm install @microsoft/signalr

npm installs the package contents in the node_modules\@microsoft\signalr\dist\browser folder. Create a new folder named signalr under the wwwroot\lib folder. Copy the signalr.js file to the wwwroot\lib\signalr folder.

Reference the SignalR JavaScript client in the script element. For example:

<script src="~/lib/signalr/signalr.js"></script>

Use a Content Delivery Network (CDN)

To use the client library without the npm prerequisite, reference a CDN-hosted copy of the client library. For example:

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.13/signalr.min.js" integrity="sha512-IS7oxIOouU8HW/57DUnTJd9AQ9nzCfdTKmYuZtYcCHaKbdS/1f3Sce+eR5qkrqMmYuGD6UE4pcutXuzgB6kGRQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

The client library is available on the following CDNs:

Install with LibMan

LibMan can be used to install specific client library files from the CDN-hosted client library. For example, only add the minified JavaScript file to the project. For details on that approach, see Add the SignalR client library.

Connect to a hub

The following snippet is the minimum required code to create and start a connection to the hub:

const connection = new signalR.HubConnectionBuilder()
.withUrl("/url")
.configureLogging(signalR.LogLevel.Information)
.build();

async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};

connection.onclose(start);

// Start the connection.
start();
Important

The client code must use an absolute URL instead of a relative URL.
Use .withUrl("https://api.skchase.com/checkout").
The hub's name is case insensitive.

Call hub methods from the client

JavaScript clients call public methods on hubs via the invoke method of the HubConnection. The invoke method accepts:

  • The name of the hub method.
  • Any arguments defined in the hub method.

The invoke method returns a JavaScript Promise. The Promise is resolved with the return value (if any) when the method on the server returns. If the method on the server throws an error, the Promise is rejected with the error message. Use async and await or the Promise's then and catch methods to handle these cases.

JavaScript clients can also call public methods on hubs via the the send method of the HubConnection. Unlike the invoke method, the send method doesn't wait for a response from the server. The send method returns a JavaScript Promise. The Promise is resolved when the message has been sent to the server. If there is an error sending the message, the Promise is rejected with the error message. Use async and await or the Promise's then and catch methods to handle these cases.

info

Using send doesn't wait until the server has received the message. Consequently, it's not possible to return data or errors from the server.

Error handling and logging

Use try and catch with async and await or the Promise's catch method to handle client-side errors. Use console.error to output errors to the browser's console:

try { await connection.invoke("SendMessage", user, message); } catch (err) { console.error(err); }

Set up client-side log tracing by passing a logger and type of event to log when the connection is made. Messages are logged with the specified log level and higher. Available log levels are as follows:

  • signalR.LogLevel.Error: Error messages. Logs Error messages only.
  • signalR.LogLevel.Warning: Warning messages about potential errors. Logs Warning, and Error messages.
  • signalR.LogLevel.Information: Status messages without errors. Logs Information, Warning, and Error messages.
  • signalR.LogLevel.Trace: Trace messages. Logs everything, including data transported between hub and client.

Use the configureLogging method on HubConnectionBuilder to configure the log level. Messages are logged to the browser console:

const connection = new signalR.HubConnectionBuilder()
.withUrl("/url")
.configureLogging(signalR.LogLevel.Information)
.build();

Browse API - GetVouchers()

In the following demo, we created a simple Web Client that can be used to get the vouchers for a given sales channel Id.

The demo Web App consists of static web content. There is no MVC, Razor, Controllers nor any other server-side logic. Instead, we have a single JavascriptClient.html and static content (such as JavaScript and CSS).

JavascriptClient.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Javascript SignalR Client</title>

<!-- JS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.13/signalr.min.js" integrity="sha512-IS7oxIOouU8HW/57DUnTJd9AQ9nzCfdTKmYuZtYcCHaKbdS/1f3Sce+eR5qkrqMmYuGD6UE4pcutXuzgB6kGRQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<!-- CSS -->
<link rel="stylesheet" href="assets/css/theme.css">
</head>
<body>
<div id="wrapper" style="margin:30px;">
<h1>Javascript Client</h1>
<br />
<h3 id="resultsCount"></h3>
<br />
<h2>Method: GetVouchers()</h2>
<br />
<form>
<div class="form-group">
<label for="txtSalesChannelId">Sales Channel ID:</label>
<input type="text" class="form-control" id="txtSalesChannelId" placeholder="Enter sales channel Id">
<br />
<p>Example salesChannelId: b6dbf562-d780-c252-518d-b6dab501bbe2 </p>
<br />
</div>
<button type="submit" id="btnInvoke" class="btn btn-primary">Call GetVouchers()</button>
</form>
<br /><br />
<h2 id="lblModel">ProductModel</h2>
<div>
<ul id="result" class="list-group"></ul>
</div>
</div>

<script src="assets/js/JavaScriptClient.js"></script>
</body>
</html>

Our API endpoint is:

https://api.skchase.com/checkout

The sample JavaScriptClient.js code below uses the Microsoft SignalR JavaScript library to create a “connection” object.

The following code:

  • Defines configuration values;
  • Identifies the API endpoint of the service - .withUrl("https://api.skchase.com/checkout");
  • Calls the signalR.HubConnectionBuilder() function to establish a connection to our SignalR Hub;
  • Uses a sales channel id that is passed as a request parameter to the corresponding GetVouchers() method from our hub;
  • We invoke (or call) the GetVouchers() method on a button click event.
    The button is disabled until the connection to the hub is established;
  • We get the response containing the array of ProductModel objects (see API Models section for more details);
  • Then we print the properties of the first object from the array and appending them to a list.

This is the sample code from our JavascriptClient.js example:

document.addEventListener("DOMContentLoaded", () => {

const connection = new signalR.HubConnectionBuilder()
.withUrl("https://api.skchase.com/checkout")
.configureLogging(signalR.LogLevel.Information)
.build();

document.getElementById("btnInvoke").disabled = true;
document.getElementById("lblModel").hidden = true;

//var salesChannelId = "b6dbf562-d780-c252-518d-b6dab501bbe2"; //example Balmoral
var salesChannelId = document.getElementById("txtSalesChannelId").value;

document.getElementById("btnInvoke").addEventListener("click", event => {
salesChannelId = document.getElementById("txtSalesChannelId").value;
if (salesChannelId) {
invoke();
async function invoke() {
try {
await connection.invoke("GetVouchers", (salesChannelId))
.then(response => {
if (response.error) {
console.warn('Error received from server: ', response.error);
}
// print the response
if (response) {
document.getElementById("resultsCount").innerText = "Number of vouchers in the result array: " + response.length;
document.getElementById("lblModel").hidden = false;

const getObjectValues = (obj) => (obj && typeof obj === 'object')
? Object.values(obj).map(getObjectValues).flat()
: [obj]

console.log(response); // first print the result to console;

var firstVoucher = response[0];
for (var prop in firstVoucher) {
var node = document.createElement("LI");
node.className = "list-group-item ";

// check for nested objects
if (typeof firstVoucher[prop] === 'object') {
var textnode = document.createTextNode(prop + ": " + '"' + getObjectValues(firstVoucher[prop]) + '"');
}
else
var textnode = document.createTextNode(prop + ": " + '"' + firstVoucher[prop] + '"');

node.appendChild(textnode);
document.getElementById("result").appendChild(node);
}
}
return;
});
}
catch (err) {
console.log(err);
setTimeout(start, 5000);
}
}
}
event.preventDefault();
});

async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};

connection.onclose(start);

// Start the connection.
start().then(function () {
document.getElementById("btnInvoke").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});;
});

You can find the complete result array of objects printed in the console.

Visit the following links for more information on how to implement other types of clients: