Skip to content

Commit

Permalink
hooks, getting started updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ntotten committed Dec 22, 2023
1 parent 99e5f52 commit bb22e7f
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 47 deletions.
89 changes: 89 additions & 0 deletions docs/articles/hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: Request/Response Hooks
---

Hooks allow running code as part of the Request/Response pipeline. Hooks are
typically added using a plugin as shown in this document, but they can also be
added globally using [Runtime Extensions](./runtime-extensions.md).

:::tip

All hooks can be either synchronous or asynchronous. To make your hook
asynchronous simply add the `async` keyword on the function.

:::

### Hook: OnResponseSending

The `OnResponseSending` hook on `ZuploContext` fires just before the response is
sent to the client. The `Response` can be modified by returning a new `Response`
from this hook. This hook is useful for creating an inbound policy that also
needs to run some logic after the response is returned from the handler.

The example below shows a simple tracing policy that adds a trace header to the
request and ensures the same header is returned with the response.

```ts
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";

export async function tracingPlugin(
request: ZuploRequest,
context: ZuploContext,
policyName: string,
) {
// Get the trace header
let traceparent = request.headers.get("traceparent");

// If not set, add the header to the request
if (!traceparent) {
traceparent = crypto.randomUUID();
const headers = new Headers(request.headers);
headers.set("traceparent", traceparent);
return new Request(request, { headers });
}

context.addResponseSendingHook((response, latestRequest, context) => {
// If the response doesn't have the trace header that matches, set it
if (response.headers.get("traceparent") !== traceparent) {
const headers = new Headers(response.headers);
headers.set("traceparent", traceparent);
return new Response(response.body, {
headers,
});
}
return response;
});

return request;
}
```

### Hook: OnResponseSendingFinal

The `OnResponseSendingFinal` hook on `ZuploContext` fires immediately after the
response is sent to the client. The `Response` in this hook is immutable and the
body has been used. This hook is useful for custom performing various tasks like
logging or analytics.

```ts
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";

export async function pluginWithHook(
request: ZuploRequest,
context: ZuploContext,
policyName: string,
) {
const cloned = request.clone();
context.addResponseSendingFinalHook(
async (response, latestRequest, context) => {
const body = await cloned.text();
await fetch("https://example.com", {
method: "GET",
body,
});
},
);

return request;
}
```
87 changes: 49 additions & 38 deletions docs/articles/runtime-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function signature.

The following configurations are available.

### Custom Problem (Error) Response Formatter
## Custom Problem (Error) Response Formatter

Zuplo includes built-in error handling that returns errors in the format of the
[Problem Details for HTTP APIs](http://httpproblems.com/) proposed standard.
Expand Down Expand Up @@ -85,64 +85,75 @@ export function runtimeInit(runtime: RuntimeExtensions) {
}
```

### Hook: OnResponseSending
## Hooks

The `OnResponseSending` hook allows modification of the `Response` immediately
before it is sent to the client. The hook provides the `Request` and `Response`
and returns a `Response`. To modify the outgoing response create and return a
`new Response()`.
Hooks allow code to be run as part of the request/response pipeline. Hooks can
be created at the API level in `zuplo.runtime.ts` as shown below or can be added
via a plugin as [documented here](./hooks.md).

The example below shows modifying the response for a specific path.
:::tip

All hooks can be either synchronous or asynchronous. To make your hook
asynchronous simply add the `async` keyword on the function.

:::

The following hooks can be set globally in the `zuplo.runtime.ts`:

### Hook: OnRequest

Runs when a request is received, before any plugins or handlers.

```ts
import { RuntimeExtensions } from "@zuplo/runtime";

export function runtimeInit(runtime: RuntimeExtensions) {
runtime.addRequestHook((request, context) => {
// Code here

// Can return a request or a response. If a response is returned the
// pipeline stops and the response is returned.
return request;
});
}
```

### Hooks: OnResponseSending

Runs before a response is sent. Response can be modified.
[More details.](/docs/articles/hooks#hook-onresponsesending)

```ts
import { RuntimeExtensions } from "@zuplo/runtime";

export function runtimeInit(runtime: RuntimeExtensions) {
runtime.addResponseSendingHook(async (response, latestRequest, context) => {
const url = new URL(request.url);
if (url.pathname === "/path-to-change-response") {
return new Response("New Response", response);
}
runtime.addResponseSendingHook((response, request, context) => {
// Code here
return response;
});
}
```

### Hook: Context OnResponseSendingFinal
### Hooks: OnResponseSendingFinal

The `OnResponseSendingFinal` hook on `ZuploContext` fires immediately after the
response is sent to the client. The `Response` in this hook is immutable and the
body has been used. This hook is useful for custom performing various tasks like
logging or analytics.
Runs before a response is sent. The response cannot be modified.
[More details.](/docs/articles/hooks#hook-onresponsesendingfinal)

```ts
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";

export async function pluginWithHook(
request: ZuploRequest,
context: ZuploContext,
policyName: string,
) {
const cloned = request.clone();
context.addResponseSendingFinalHook(
async (response, latestRequest, context) => {
const body = await cloned.text();
await fetch("https://example.com", {
method: "GET",
body,
});
},
);
import { RuntimeExtensions } from "@zuplo/runtime";

return request;
export function runtimeInit(runtime: RuntimeExtensions) {
runtime.addResponseSendingFinalHook((response, request, context) => {
// Code here
});
}
```

## Plugin and Handler Extensions

Built-in and custom plugins and handlers can expose their own extensibility. The
[AWS Lambda handler](../handlers/aws-lambda.md) exposes the ability to customize
the event that is sent when invoking the Lambda function.
Built-in and custom plugins and handlers can expose their own extensibility. For
example, [AWS Lambda handler](../handlers/aws-lambda.md) exposes the ability to
customize the event that is sent when invoking the Lambda function.

The example below shows how to use a route's custom property to set the path on
the outgoing event to a custom value.
Expand Down
12 changes: 10 additions & 2 deletions docs/articles/step-1-setup-basic-gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ the key is provided in the unauthenticated error message.
To get started, sign in to [portal.zuplo.com](https://portal.zuplo.com) and
create a free account. Create a new **empty** project. Then...

:::tip Local Development

Zuplo also supports building and running your API locally. To learn more
[see the documentation](./local-development.md).

:::

## 1/ Add a route

Inside your new project, choose the `routes.oas.json` file and click **Add
Expand All @@ -33,9 +40,10 @@ Save your changes (you can click the disk icon next to `routes.oas.json` or
press CMD+S).

You can quickly test this route by clicking the **Test** button next to the
**Path** field and clicking the URL in the dialog that opens.
**Path** field. You can use the built in test tool or click the URL to open in a
new tab.

![Test](https://cdn.zuplo.com/assets/cd094b3c-efbe-4c2b-995c-60ce0302704a.png)
<Screenshot src="https://cdn.zuplo.com/assets/ef3005b5-2e69-4704-bcfe-12b2db62e0b4.png" />

You should receive a 401 Unauthorized that says something similar to

Expand Down
15 changes: 8 additions & 7 deletions docs/articles/step-2-add-api-key-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ JSON. Note,
The API key auth policy should usually be one of the first policies in your
request pipeline, drag it to the top if you have multiple policies.

<Screenshot src="https://cdn.zuplo.com/assets/8f698429-f265-40d9-99d7-156b28b7ef1b.gif" size="sm" />
:::

<Screenshot src="https://cdn.zuplo.com/assets/8f698429-f265-40d9-99d7-156b28b7ef1b.gif" size="sm" />

If you test your route, you should get a 401 Unauthorized response

```
Expand Down Expand Up @@ -68,25 +69,25 @@ Since we need to send the key in a header, it's hard to use the browser for this
test. We'll use our built in test client in Zuplo but you could also use Postman
for this part.

Go to the API Test Console and create a new **Manual Test**. Set the **path** to
`/todos` and hit **Test**.
Next to the path of your route in Route Designer click the **Test** button. Set
the **path** to `/todos` and hit **Test**.

![Test Console](https://cdn.zuplo.com/assets/611050a5-257e-4594-a914-1da68d504371.png)
<Screenshot src="https://cdn.zuplo.com/assets/c2db1247-eb72-474d-bfed-8b14b3b62b5e.png" />

You should get a 401 Unauthorized response. Add an new `authorization` header
with the value `Bearer YOUR_API_KEY` and insert the API Key you got from the
developer portal.

You should now get a 200 OK.

![200 OK](https://cdn.zuplo.com/assets/82823f9d-62ad-4f18-9e07-bb89dc9ad32d.png)
<Screenshot src="https://cdn.zuplo.com/assets/87c03fc4-4525-43dd-8eb7-15808b545fef.png" />

:::note

We also offer an API for our API key service that allows you to programmatically
create consumers and even create your own developer portal or integrate key
management into your existing dashboard. Contact us at `[email protected]` for
access.
management into your existing dashboard. See
[this document for details](./api-key-api.md).

:::

Expand Down
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ const sidebars = {
"articles/runtime-behaviors",
"articles/zp-body-removed",
"articles/audit-log",
"articles/hooks",
"articles/runtime-extensions",
"articles/not-found-handler",
],
Expand Down

1 comment on commit bb22e7f

@vercel
Copy link

@vercel vercel bot commented on bb22e7f Dec 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./

docs-git-main.zuplopreview.net
docs.zuplopreview.net
docs.zuplo.site

Please sign in to comment.