HTTP Functions are one of a few ways to use Velo to extend the functionality of the backend portion of your Wix Studio site. Some others are data hooks, job schedulers and web modules, all of which have their own uses.
HTTP Functions in particular are used to create RESTful Application Programming Interfaces (APIs), which allow third-party sites to use the backend of your site in a clean, modular way.
Where a human user might primarily interact with the frontend of your website, APIs make the backend of your website accessible to third-party clients in code. These sites can then read data from your database collection, write to that collection, submit order information, and do a variety of other tasks supported by Velo and JavaScript. They can be a powerful tool in your web development kit, and their power requires that they be properly secured.
Secure Communication Between Hosts and Consumers
Some APIs are intended for internal cross-site communication, and leaving them open to the entire world can be a recipe for disaster. We certainly don’t want any random person reading or writing data to our websites. We want to ensure that only parties we trust not to misuse our APIs are able to access our database collection. To do this, we’ll be leveraging the HMAC Authentication Velo package. Hash-based message authentication code (HMAC) allows you to use a shared secret to authenticate requests between two websites.
For our example, we’ll call one website the Host site, which will contain our API endpoint.We’ll call the second website our Consumer site. This site will call the API endpoint and, in this case, read data from the Host site.
It’s worth noting that this 1 Host -> 1 Consumer topology is the simplest example we can create. In more complex instances, you may encounter 1 Host -> many Consumers, or even websites that function as Hosts in some contexts and Consumers in others.
These topologies won’t be discussed in-depth here, but it’s important to understand that when we say Host and Consumer, we are talking about a relationship between two sites, rather than inherent properties of the sites themselves. To put it simply: a Host has data or functionality that a Consumer needs.
Setting Up the Host and Consumer
There are a few steps that need to be done on both the Host and Consumer. Both sites will need to use the same HMAC Authentication package, and share the same secret phrase, so we’ll do a couple things on both of them:
1. On your Host site click on the code panel ({}) on the left side of your screen. Choose “Packages & Apps” followed by “Install Velo Package”, type “hmac” into the search bar, and click “Install” for the “@velo/wix-http-functions-hmac-authentication-backend” package. Then, do the same on your Consumer site.
2. Create a secret string, this can be done in a few different ways:
Create a long random string yourself
On Mac or Linux, you can use the command: `head -n 4096 /dev/urandom | openssl sha256`
On Windows, you can use the command: `Get-FileHash -InputStream ([System.IO.MemoryStream]::New([System.Text.Encoding]::ASCII.GetBytes(-join ((1..256) | %{Get-Random -minimum 0 -maximum 255 }))))`
3. Store this secret in your Secret Manager on both the Host and Consumer sites. You can find this on each site’s Dashboard, under Developer Tools > Secrets Manager > Store Secret
Name it `hmac-authentication-secret-key`. This will allow us to retrieve the value of this secret using its name later on in our code.
Paste the secret key we generated in step 2 into the value box and click Save.
Setting Up Your Host Site to Validate Requests
Now, we’ll create an http-functions.js file on the backend of our Host site. In this case, our Host site will have data (user phone numbers) that our Consumer site wants, but that we don’t want to expose to the rest of the world.
To do this, navigate to your code panel, select “Backend code”, and click “Open Wix IDE” which will launch a new window with our Wix IDE editor. Then under select the backend folder and create a new file within it by right clicking the “backend” folder and choosing “New File…”
We’ll want to use the specific name `http-functions.js`, so that Velo knows that this file has HTTP/API functions defined in it. HTTP functions also have specific naming conventions, which you can read about in the wix-http-functions documentation along with other considerations.
The full code to put in your `backend/http-functions.js` file on the Host site is:
import { ok, badRequest } from "wix-http-functions";
import { validateAuth } from "@velo/wix-http-functions-hmac-authentication-backend";
// Define some simple default response objects
const defaultResponse = {
headers: {
"Content-Type": "application/json"
},
};
const defaultInvalidAuthResponse = {
...defaultResponse,
body: {
error: "Invalid authentication token"
}
}
export async function get_siteStats(request) {
// Check Authentication before doing anything else!
try {
await validateAuth(request, { secretName: "hmac-authentication-secret-key" });
} catch (err) {
return badRequest(defaultInvalidAuthResponse);
}
let sensitiveData = {
userCount: 100,
monthlyRevenue: 80091.23,
};
let response = defaultResponse;
response.body = {
data: sensitiveData,
};
console.log("Sent sensitive data to Consumer");
return ok(response);
}
Validating a Request’s Authentication
As you can see in the code above the Host function `get_siteStats` will return the `sensitiveData` when called by the Consumer. At the start of the function we’ll call `validateAuth` which comes from our HMAC library to ensure that the requests we receive are properly authenticated. This function throws an error when it receives an invalid request, which is why we wrap it in a `try/catch` block so that we can send a response back to the requestor if the request is not properly authenticated.
If the request fails here the requestor will see a response like the below.
We also have the option to specify the name of the secret we’re using in our Secret Manager. I’m using the default name to demonstrate (`{ secretName: 'hmac-authentication-secret-key' }`), but you can name it whatever you want in the Secret Manager as long as you make sure to specify it here.
Putting It All Together
Lastly, we’ll do the rest of the work needed to return our sensitive data to the client. Normally this would be something like a database query but here we just return a static bit of JSON to demonstrate. This is another reason why we want to `validateAuth` at the start of our function, database queries can be time consuming to run, and before we do any actual work we want to first make sure we’re doing that work for an authenticated Consumer.
Implementing the Consumer Side
Now that our Host site is set up to validate requests, we’ll need to set up our Consumer site to send valid requests to the Host site. This is a good time to double-check to confirm that you did the first few steps of this article correctly on both your Host and Consumer sites (i.e. setting up the shared secret in your Secret Manager and installing the hmac library).
Once you’ve confirmed that you can use the following code to make a request from the Consumer site. This process is much shorter than setting up the Host site. We’ll want to put this code in a new file in our backend folder within the Wix IDE. We make it a Web Module by naming it `backend.web.js`.
Note that we’re demonstrating the Consumer side of things through Web Modules but in practice we’d have many options for how we get data from our Host such as setting up a scheduled job, tying it into a data hook, or performing any other backend-only operation where you need data from the Host site.
The full code to put in your `backend/backend.web.js` file on the Consumer site is:
import { invoke } from '@velo/wix-http-functions-hmac-authentication-backend'; import { Permissions, webMethod } from "wix-web-module"; export const siteStats = webMethod(Permissions.Admin, async () => { // Where our API endpoint is on the Host site const endpointUrl = 'https://YOURSITE/_functions/siteStats'; const fetchOptions = { method: 'get' } const hmacOptions = { secretName: 'hmac-authentication-secret-key' } let response = await invoke(endpointUrl, fetchOptions, hmacOptions); return response.json(); });
First, we’ve defined siteStats as a web method, and since it involves sensitive data we’ll make sure the permissions are set to Admin only.
Within the function we’ll need to define our `endpointUrl`, the format of which is specified in the wix-http-functions documentation. You’ll need to adjust this to correspond to the URL of your website.
Then we’ll define our `fetchOptions` function and specify that we’re going to use the HTTP method `get`. You’ll notice that this matches the definition of our function on the Host site (i.e. `get_siteStats`). Lastly, we define our `hmacOptions` variable to specify the `secretName` of the shared secret we stored in the Secret Manager earlier.
Once we’ve defined this function and variable, we can call the `invoke` function with our options passed in as a parameter and it will handle creating an authenticated request to the Host site. Then we’ll call `response.json()` after the Promise resolves and return the response body.
[Related: How to adapt to a cookie-free future]
Testing It Out
Alright now let’s see if our code works and our two sites can communicate. To do this we’ll add a little bit of code to our homepage and then click on the eye icon for preview mode.
import { siteStats } from "backend/backend.web";
$w.onReady(function () {
console.log("Requesting data from the Host site through the Consumer site's backend...")
siteStats().then(result => console.log(result));
});
After the page loads, we can see the request was made and successfully returned in the console.
And that’s all there is to it! If you have any questions about how HMAC or HTTP Functions work, make sure to visit the Velo API docs. If you hit any snags or have any questions, feel free to ask on the Wix Studio forum.