> Documentation index: [Saleor](/llms.txt) · [This section](/developer/llms.txt)
> Source: https://docs.saleor.io/developer/extending/apps/developing-apps/app-sdk/api-handlers

# API Handlers

App SDK mainly targets serverless environments, like Vercel functions or AWS Lambda. It provides a set of handlers that can be used to build Saleor apps.

SDK providers helpers for following types of functions (that represent API endpoints):

-   [Manifest handler](/developer/extending/apps/architecture/app-requirements.md#manifest-url) - Used to fetch app manifest by Saleor during app installation.
-   Register handler - Used to register app in Saleor during installation and save the token.
-   Webhook handler - Exposes endpoint that Saleor will call with webhook events.
-   Protected handler - Endpoints meant to be allowed only by the App's frontend (from Saleor Dashboard).

<a id="required-handlers"></a>

## Required handlers

Saleor requires the following endpoints for a standalone app:

-   Manifest endpoint - Returns JSON object with app properties, like its name or permissions. [Read more](/developer/extending/apps/architecture/manifest.md)
-   Register endpoint - If your app needs to access to protected API, you must provide `tokenTargetUrl` in the manifest and implement a register endpoint to handle the `POST` request with the token. [Read more](/developer/extending/apps/installing-apps.md#installation-using-graphql-api)

<a id="built-in-api-handlers"></a>

## Built-in API handlers

To hide Saleor's internal logic, app-sdk provides handlers factories. They should work with minimal configuration, leaving App creators space for domain logic.

<a id="manifest-handler-factory"></a>

### Manifest handler factory

Here is an example usage of a manifest handler in Next.js:

```typescript
// pages/api/manifest.ts

// Change "next" to other platforms if needed
import { createManifestHandler } from "@saleor/app-sdk/handlers/next";

export default createManifestHandler({
  manifestFactory({ request, appBaseUrl, schemaVersion }) {
    return {
      name: "My Saleor App",
      // tokenTargetUrl is optional. Omit it if your app doesn't need the token.
      tokenTargetUrl: `${appBaseUrl}/api/register`,
      appUrl: appBaseUrl,
      permissions: ["MANAGE_USERS"],
      id: "my-saleor-app",
      version: "1",
    };
  },
});
```

`<PLATFORM>` is one of the supported platforms, like NextJS or Lambda

Options provided to handler factory:

```typescript
type CreateManifestHandlerOptions = {
  manifestFactory(context: {
    appBaseUrl: string;
    request: Request; // Depends on the platform, e.g. Request, NextApiRequest, NextRequest
    schemaVersion: [major: number, minor: number] | null;
  }): AppManifest; // Ensures response type is valid
};
```

You can use `request` to read additional parameters from the request.

Field `schemaVersion` can be used to enable some feature based on the Saleor version. It will be `null` if request doesn't contain `saleor-schema-version` header. Saleor will automatically attach this header, but the GET request executed e.g. from the browser will not contain this field.

Hint: `@saleor/app-sdk` contains documented types attached to the npm package.

<a id="app-register-handler-factory"></a>

### App register handler factory

Following example shows how to use a register handler in Next.js:

```typescript
// pages/api/register.ts - next.js route

import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next";
import { UpstashAPL } from "@saleor/app-sdk/APL/upstash"; // See APL section

export default createAppRegisterHandler({
  apl: new UpstashAPL({
    restURL: "...",
    restToken: "...",
  }),
  allowedSaleorUrls: ["https://your-saleor.saleor.cloud/graphql/"], // optional, see options below
  async onRequestVerified(req, { authData, respondWithError }) {
    await doSomethingAndBlockInstallation(authData.token).catch((err) => {
      // Return this method to break installation flow and show error in the Dashboard
      return respondWithError({ message: "Error, installation will fail" });
    });
  },

});
```

Options provided to handler factory

```typescript
export type CreateAppRegisterHandlerOptions = {
  apl: APL;
  /**
   * Provide your Saleor /graphql/ endpoints (or functions),
   * to allow app registration only in allowed Saleor instances.
   */
  allowedSaleorUrls?: Array<string | ((saleorApiUrl: string) => boolean)>;
  /**
   * Optional
   * Run right after Saleor calls this endpoint
   */
  onRequestStart?(
    request: Request, // Can be different depending on the platform
    context: {
      authToken?: string;
      saleorDomain?: string;
      saleorApiUrl?: string;
      respondWithError: ({ status, message }) => never; // will throw
    }
  ): Promise<void>;
  /**
   * Optional
   * Run after all security checks
   */
  onRequestVerified?(
    request: Request,
    context: {
      authData: AuthData;
      respondWithError: ({ status, message }) => never; // will throw
    }
  ): Promise<void>;
  /**
   * Optional
   * Run after APL successfully AuthData, assuming that APL.set will reject a Promise in case of error
   */
  onAuthAplSaved?(
    request: Request,
    context: {
      authData: AuthData;
      respondWithError: ({ status, message }) => never; // will throw
    }
  ): Promise<void>;
  /**
   * Optional
   * Run after APL fails to set AuthData
   */
  onAplSetFailed?(
    request: Request,
    context: {
      authData: AuthData;
      error: unknown;
      respondWithError: ({ status, message }) => never; // will throw
    }
  ): Promise<void>;
};
```

See [APL](/developer/extending/apps/developing-apps/app-sdk/apl.md) for details on what is Auth Persistence Layer in Saleor apps.

<a id="async-webhook-handler"></a>

### Async Webhook Handler

App SDK provides a utility that helps build (async) webhook handlers so that the app can react to Saleor events.

Read about it [here](/developer/extending/apps/developing-apps/app-sdk/saleor-webhook.md).

<a id="protected-handler"></a>

### Protected handler

To protect endpoint from the outside world and accept only requests from the app's frontend, use the `createProtectedHandler` function. See more details [here](/developer/extending/apps/developing-apps/app-sdk/protected-handlers.md).
