Skip to main content

Webhooks

Webhooks provide a way for external APIs to notify you when some event has occurred in their system. That is a typical use case for this API.

These APIs are generally not secure. Different providers have different mechanisms for proving to you that the request is genuinely from them. Hypi provides you access to all the data sent in the request, this enables you to perform verification as you see fit.

The webhook can handle events of any type like payment failure, notification for a new post on the blog, reporting of an issue in the system, etc. And the response to the event could be an automated email or storing the data in the database. The response can be customized based upon the status code sent through the HTTP request.

Let's say a payment system wants to notify the failure of Payments. So, the payment system sends an automated message or payload to the App Instance through an HTTP POST. A function defined in the App Schema process the webhook request. It parses the payload and extracts the data from it. The payload type is usually JSON. But some providers may send XML or URL encoded form data.

If the data of the payload has an email ID of the customer whose payment failed, a trigger can be invoked to send an email to him automatically.

Let’s define a function myFn in the schema that processes a webhook request.

caution

For the webhook to correct properly, the signature of the function processing webhook request must be (payload: WebhookPayload): WebhookResponse

type Query {
myFn(payload: WebhookPayload): WebhookResponse @fn(name:"my_webhook", version: "v1", env: ["abc"])
}

Here, serverless function my_webhook executed the webhook request by processing payload.

Sample webhook function processing the payload can be written in NodeJS as follows.

function hellWorld(input, callback) {
console.log('Yeah, we got some input', input, input.env, input.args);
let payload = JSON.parse(input.args.payload.toString('utf-8'));
callback(
{
"status": 200,
"headers": {"My-Header": "my header value"},
"body": JSON.stringify({
"my": "JSON object as a string",
"host": payload.url.host,
"port": payload.url.port,
"params": payload.url.queryParams,
"body": payload.body,
})
}
, null
);
}

// Must export `main`
exports.main = hellWorld;

Detailed instructions are given in this README file on how to deploy and execute serverless functions.

You may also follow below steps to deploy my_webhook serverless function.

First, authenticate Docker with Hypi Container Registry by login. Use Authorization token from developer console as password.

docker login hcr.hypi.app -u hypi

To build the docker image, use the following command.

docker build --platform=linux/amd6 . -t hcr.hypi.app/my_webhook:v1

--platform=linux/amd6 parameter is applicable while using a platform other than linux. This means you can run this command on windows and Mac (including M1 chips).

Now let’s deploy the serverless function on Hypi’s low code backend

docker push hcr.hypi.app/my_webhook:v1

Our serverless function is ready to use!

Now, let's go through the data types related to Webhook.

type WebhookPayload {
url: URLInput!
headers: Json!
body: String
}

Webhookpayload data type stores the data of a payload. A payload has url string along with a header and body.

ParameterTypeDescription
urlURLInputInput URL provided by the payload
headersJSONHeader of the Payload HTTP request/response
bodyStringData Attached to Payload

Input url contains url information like url path, query parameters, port, host, etc.

type WebhookResponse {
status: Int
headers: Json
body: Json
}

If the query or mutation functions in the Webhook definition (myInlineFn) returns this then it controls what the server responds with. For example, the GraphQL function can return a 301 or 302 status and a Location header to an external URL to cause a redirect. If status 200 gets returned, it indicates the success of the event. You may extract the status by parsing the input HTTP POST request.

You can design the myFn as per the requirements. It takes the shape of Serverless Function. But as stated above, its signature remains the same. In the current serverless function, it returns HTTP request status as 200 and it separates the payload header and body. The URL input parameters of the payload get attached to the payload body forming a response string.

Example

type Webhook {
name: String
as: Account
query: GraphQLRef!
}

Webhook type is defined in Hypi as above. A webhook object needs to be created in an instance to send/receive a webhook HTTP request/response through an endpoint. The parameters of the Webhook type are as follows.

ParametersTypeDescription
nameStringThe name of the webhook. It is referenced in the URL by this name. If missing the webhook is only addressable by ID
asAccountDefaults to the account creating the Webhook. Hypi will generate an authorization token automatically for the account when the webhook is triggered.
queryGraphQLRefThis refers to a GraphQL function to process webhook requests. The function can trigger a workflow or operate on the payload itself.

Let’s check GraphQLRef type as well

type GraphQLRef {
type: OpType!
field: String!
selection: String
}
ParametersTypeDescription
typeOpTypeQuery/Mutation/Subscription
fieldStringThe name of the function that processes the webhook request.
selectionStringIf present this is a set of GraphQL fields that will be selected from the results of the function that is referenced.

A simple webhook example looks like this.

mutation Upsert($values: HypiUpsertInputUnion!) {
upsert(values: $values) {
id
}
}

Once a Webhook is created, it can be called by making an HTTP request to:

where {domain} is the instance domain for your app and {webhook name} is the name of the webhook to be executed. In the example on this page, the name is wh1.

Ex: https://api.hypi.app/webhook/incineration.apps.hypi.app/wh1

Before sending Webhook Request, make sure to Enable Anonymous Requests in the API Configurations of an Instance.

The result of the execution of webhook request:

{
"status": 200,
"headers": {
"My-Header": "my header value"
},
"body": "{\"my\":\"JSON object as a string\",\"host\":\"api.hypi.app\",\"port\":443,\"params\":{},\"body\":\"{\\n \\\"ahoy\\\": \\\"matey\\\"\\n}\"}"
}