- Shorthand HTTP requests - Easy to use, but limits what you can control. Best for simple requests.
- Manual HTTP requests - Gives you full control over the request and response.
requestTemplate- an object literal of HTTP request options that will be merged with every request.beforeRequest- middleware that mutates every request before it is sent.afterResponse- middleware that mutates every response before it is completed.
You can install any HTTP client you like—but this is discouraged as you might lose automatic HTTP logging and middleware.
Shorthand HTTP requests
For simple HTTP requests that do not require special pre- or post-processing, you can specify the HTTP request options as an object literal in your app definition. This features:- Lazy
{{curly}}replacement. - JSON and form body de-serialization.
- Automatic non-2xx error raising.
{{bundle.authData.subdomain}} is automatically replaced with the live value from the bundle. If the call returns a non-2xx status code, an error is automatically raised. The response body is automatically parsed as JSON or form-encoded and returned.
An error will be raised if the response cannot be parsed as JSON or form-encoded. To use shorthand requests with other response types, add middleware that sets response.data to the parsed response.
Manual HTTP requests
Use this when you need full control over the request/response. For example:- To do processing (usually involving
bundle.inputData) before a request is made. - To do processing of an API’s response before you return data to Zapier.
- To process an unusual response type, such as XML.
z.request() then use the resulting response object to return the data you want:
url above is a template literal, which is JavaScript feature and is evaluated immediately when that line of code is executed.
When to use template literals or {{curlies}}?
You will see both template literals ${var} and (double) “curlies” {{var}} used in examples.
Template literals (like ${var} or plain var) get evaluated as soon as the line of code is executed. During build time (zapier build and zapier push), the CLI tool imports your JavaScript module tree to generate a definition.json file, which is used to tell Zapier what triggers and actions your integration has.
If you use a placeholder like ${process.env.VAR} at the module level (e.g., in a shorthand request), it will be substituted with your local environment’s value for VAR and saved to definition.json. This means the value you set via zapier env:set won’t be used in some occasions in production. So just keep in mind:
Rule of thumb: Use
${var} or access var directly inside functions. Use {{var}} in shorthand requests.const val = "a" + b + "c" is essentially the same as:
Since v17,
z.request() no longer replaces {{var}} and will throw an error if there’s {{var}} in the request object.POST and PUT requests
To POST or PUT data to your API you can do this:You don’t need to serialize your request data using
JSON.stringify() before setting the body. z.request() does that for you.Using HTTP middleware
HTTP middleware is a function or piece of code that sits between a client request and the server response, allowing you to inspect, modify, or handle the request or response before they reach their destination. You use middleware to perform common tasks like adding security headers, logging requests, handling errors, or modifying data in a centralized way without repeating code. Common examples include adding a header to all outgoing responses to improve security, or catching and handling weird errors so that users receive a friendly error message instead of the system-generated message. To process all HTTP requests in a certain way, use thebeforeRequest and afterResponse middleware functions.
Middleware functions go in your app definition:
beforeRequest middleware function takes a request options object, and returns a (possibly mutated) request object. An afterResponse middleware function takes a response object, and returns a (possibly mutated) response object. Middleware functions are executed in the order specified in the app definition, and each subsequent middleware receives the request or response object returned by the previous middleware.
Middleware functions can be asynchronous - just make the middleware function async or return a promise.
The second argument for middleware is the z object, but it does not include z.request() as using that would easily create infinite loops.
Here is the full request lifecycle when you call z.request({...}):
- set defaults on the
requestobject - run your
beforeRequestmiddleware functions in order - add applicable auth headers (e.g. adding
Basic ...forbasicauth), if applicable - add
request.paramstorequest.url - execute the
request, store the result inresponse - try to auto-parse response body for non-raw requests, store result in
response.data - log the request to Zapier’s logging server
- if the status code is
401, you’re using a refresh-able auth (such asoauth2orsession) andautoRefreshistruein your auth configuration, throw aRefreshAuthError. The server will attempt to refresh the authentication again and retry the whole step - run your
afterResponsemiddleware functions in order - call
response.throwForStatus()unlessresponse.skipThrowForStatusistrue
z.request().
Check out this example for a working example integration using HTTP middleware.
Error response handling
z.request() has some built-in middleware to help you handle error responses in the most sensible way. But in different major core versions, it acts a little differently. Here’s a diagram to illustrate that:
v10.x and above: automatic throw for error status
If you’re using core v10.x and above, you don’t need to manually handleresponse.status >= 400 or call response.throwForStatus() after every z.request() call, as the built-in throwForStatus middleware will do that for you.
However, you can disable automatic error throwing by setting skipThrowForStatus on the request object:
afterResponse runs before the built-in throwForStatus middleware, you can also “hijack” the error response in your afterResponse. For example, if the API uses a status code ≥ 400 that should not be treated as an error, you can do this:
response.throwForStatus() or throw an error yourself, likely following the z.request() call.
v12.x and above: the built-in throwForStaleAuth middleware
In v12.x, we brought back the built-in throwForStaleAuth middleware that throws z.errors.RefreshAuthError when a 401 Unauthorized response is received and your authentication supports auto-refresh (such as oauth2 or session auth).
This means you’d never see a 401 response in your afterResponse middleware if your authentication type is oauth2 or session and you enable auoRefresh. The 401 response would be handled by the built-in throwForStaleAuth middleware before your afterResponse can see it.
We added the built-in throwForStaleAuth middleware because we’ve seen developers’ afterResponse unintentionally “swallow” the 401 response and prevent the auth refresh from happening. For example, if you had an afterResponse middleware on v11.x as follows, autoRefresh would not work:
v18.x and above: the built-in throwForThrottling middleware
In v18.x, we added a built-in throwForThrottling middleware that throws z.errors.ThrottledError when a 429 Too Many Requests response is received. ThrottledError tells Zapier to retry the request after some time.
This means in ≥ v18 you’d never see a 429 response in your afterResponse middleware, unless you set throwForThrottlingEarly to false globally or in the z.request() options.
To set throwForThrottlingEarly globally, add it to App.flags:
throwForThrottlingEarly per request, add it to the z.request() options:
throwForThrottlingEarly or use v17.x and below, make sure you don’t unintentionally swallow 429 responses in your afterResponse middleware. For example, the following code would prevent Zapier from retrying the request:
HTTP Request Options
Shorthand requests and manual requests support the following HTTPoptions:
url: HTTP url, you can provide it as a separate argument (z.request(url, options)) or as part of theoptionsobject (z.request({url: url, ...})).method: HTTP method, default isGET.headers: request headers object, format{'header-key': 'header-value'}.params: URL query params object, format{'query-key': 'query-value'}.body: request body, can be a string, buffer, readable stream or plain object. When it is an object/array and theContent-Typeheader isapplication/x-www-form-urlencodedthe body will be transformed to query string parameters, otherwise we’ll set the header toapplication/json; charset=utf-8and JSON encode the body. Default isnull.allowGetBody: includebodyinGETrequests. Set totrueto enable. Default isfalse. Set only if required by the receiving API. See section 4.3.1 in RFC 7231.json: shortcut object/array/etc. you want to JSON encode into body. Default isnull.form: shortcut object. you want to form encode into body. Default isnull.raw: set this to stream the response instead of consuming it immediately. Default isfalse.redirect: set tomanualto extract redirect headers,errorto reject redirect, default isfollow.follow: maximum redirect count, set to0to not follow redirects. default is20.compress: support gzip/deflate content encoding. Set tofalseto disable. Default istrue.agent: Node.jshttp.Agentinstance, allows custom proxy, certificate etc. Default isnull.timeout: request / response timeout in ms. Set to0to disable (OS limit still applies), timeout reset onredirect. Default is0(disabled).signal(added in v15.14.1): enables cancelling requests via a timeout set by anAbortController. More details innode-fetchdocs here. Default isnull.size: maximum response body size in bytes. Set to0to disable. Default is0(disabled).skipThrowForStatus(added in v10.0.0): don’t callresponse.throwForStatus()before resolving the request withresponse. See HTTP Response Object.throwForThrottlingEarly(added in v18.0.0): set tofalseto disable the built-inthrowForThrottlingmiddleware that throwsz.errors.ThrottledErroron429 Too Many Requestsresponses. See HTTP middleware for more details. Default istrue.
HTTP Response Object
The response object returned byz.request([url], options) supports the following fields and methods:
status: The response status code, i.e.200,404, etc.content: The response content as a String. For Buffer, tryoptions.raw = true.data(added in v10.0.0): The response content as an object if the content is JSON orapplication/x-www-form-urlencoded(undefinedotherwise).headers: Response headers object. The header keys are all lower case.getHeader(key): Retrieve response header, case insensitive:response.getHeader('My-Header')skipThrowForStatus(added in v10.0.0): don’t callthrowForStatus()before resolving the request with this response.throwForStatus(): Throws an error if400 <= statusCode < 600.request: The original request options object (see above).
request.raw is true, the raw response has the following properties:
json(): Get the response content as an object, ifoptions.raw = trueand content is JSON (returns a promise).undefinedin non-raw requests.body: A stream available only if you provideoptions.raw = true.