> To build a Wix site agentically, follow https://www.wix.com/headless/skill.md
> 
> Portal Navigation:
> 
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - If a reference page seems to be missing its schema (parameters, return types, or type definitions), append `?schema=true` to its `.md` URL (e.g. `https://dev.wix.com/docs/contexts/contexts/stores/category-info/use-category-info-context.md?schema=true`).
> - Top-level index of all portals: https://dev.wix.com/docs/llms.txt
> - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt

## Resource: Migrate from Web Methods to HTTP Endpoints

## Article: Migrate from Web Methods to HTTP Endpoints

## Article Link: https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/migrate-an-app-to-the-new-cli/migrate-an-app-from-the-legacy-cli/migrate-from-web-methods-to-http-endpoints.md

## Article Content:

# Migrate from Web Methods to HTTP Endpoints

Migrate web methods to [HTTP endpoints](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/supported-extensions/backend/http-endpoints/about-http-endpoints.md) in your new Wix CLI project, as part of [migrating an app from the legacy Wix CLI](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/migrate-an-app-to-the-new-cli/migrate-an-app-from-the-legacy-cli/migrate-a-legacy-cli-app-to-the-new-wix-cli.md).

## Folder structure

| Legacy path | New path |
|--------|-----|
| `src/backend/<module>.web.ts` | `src/api/<endpoint>.ts` |
| `src/backend/<sharedModule>.ts` | `src/lib/<sharedModule>.ts` |
| `src/lib/<sharedModule>.ts` | `src/lib/<sharedModule>.ts` |

A legacy `.web.ts` file is named after its module and can export several web methods. An endpoint file is named after the route it serves and holds the handlers of that route. Step 1 explains how to map one to the other.

## Step 1 | Relocate your web method files

To relocate your web methods:

1. In your new Wix CLI project, create an endpoint file in `src/api/` for each web method, or for each resource, as described below. The file name sets the route, so `src/api/multiply.ts` serves `/multiply`.
2. If your web methods import shared backend modules, copy those modules into `src/lib/` in the new project, and update the import paths. Shared modules aren't extensions, so they don't belong under `src/api/`.
3. If a `.web.ts` file also exports functions that aren't web methods, don't turn them into endpoints. If other backend code uses them, move them into a shared module in `src/lib/`. If nothing uses them, leave them out.
4. Shared code outside `src/backend/` that both your backend and your extensions import, such as `src/consts.ts` or `src/types.ts`, keeps its path. See [Migrate the backend](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/migrate-an-app-to-the-new-cli/migrate-an-app-from-the-legacy-cli/migrate-a-legacy-cli-app-to-the-new-wix-cli.md#step-3--migrate-the-backend) in the main migration guide.

If one legacy file exports several web methods, you can't map them all into a single endpoint file, because an endpoint file exports at most 1 handler per HTTP method. Choose one of the following:

- Create one endpoint file per web method.
- Create a single endpoint file for the resource, and give each operation a different HTTP method, for example `GET` to read, `POST` to update, and `DELETE` to clear.


## Step 2 | Replace the import statement

Replace the `@wix/web-methods` import statement with the `APIRoute` type.

For example, the web method file uses this import:

```ts
import { webMethod, Permissions } from '@wix/web-methods';
```

HTTP endpoints use the following import:

```ts
import type { APIRoute } from '@wix/custom-extensions/types';
```

## Step 3 | Replace web method exports

To replace your web method exports:

1. Replace each `webMethod()` export with an HTTP method handler.
2. Each handler receives a request object. Read arguments from the `request` object. For example, extract values from the query string for `GET` requests, or from the request body for `POST` requests.
3. Implement the relevant logic and return a `Response` object with the result.
4. Return a `Response` on every path through the handler, including error paths. The `APIRoute` type requires it, so the typecheck fails with `Type '...' is not assignable to type 'APIRoute'` for a handler that returns anything else on some path. For a web method that returns nothing, return `new Response(null, { status: 204 })`.

For example, the web method file exports the following:

```ts
import { webMethod, Permissions } from '@wix/web-methods';

export const multiply = webMethod(
  Permissions.Anyone,
  (a: number, b: number) => a * b,
);
```

The corresponding HTTP endpoint exports the following:

```ts
import type { APIRoute } from '@wix/custom-extensions/types';

export const POST: APIRoute = async ({ request }) => {
  const { a, b } = await request.json();
  return new Response(JSON.stringify({ result: a * b }));
};
```

## Step 4 | Manage permissions

Web methods specify [permissions](https://dev.wix.com/docs/build-apps/develop-your-app/extensions/backend-extensions/apis/web-methods/about-web-method-extensions.md#web-method-permissions) using the `Permissions` enum from `@wix/web-methods`. HTTP endpoints have no equivalent setting, so an endpoint is reachable by any caller until you check the caller yourself. To restrict access, use [Get Token Info](https://dev.wix.com/docs/sdk/core-modules/essentials/auth.md#gettokeninfo) to read the caller's token and inspect its `subjectType`.

<blockquote class="warning">

__Warning:__
If your web method used any permission other than `Permissions.Anyone`, skipping this step removes that restriction. An admin-only operation becomes callable by anyone.

</blockquote>

The following table shows the check to add for each permission:

| Web method permission | Endpoint check |
|---|---|
| `Permissions.Anyone` | None. The endpoint is open to any caller either way. |
| `Permissions.SiteMember` | Reject a caller whose `subjectType` isn't `MEMBER` |
| `Permissions.Admin` | Reject a caller whose `subjectType` isn't `USER` |

> **Note:** 
> To enforce permissions, use [`httpClient.fetchWithAuth()`](https://dev.wix.com/docs/sdk/core-modules/essentials/http-client.md#fetchwithauth) when [calling from your frontend](#update-your-frontend-call-sites-as-you-migrate-each-extension). This passes your access token in the request to your HTTP endpoint. Without this, the request arrives unauthenticated and Get Token Info reports a visitor rather than the signed-in user, so a check against `subjectType` rejects it.  

For example, the following web method limits access to site members:  

```ts
import { webMethod, Permissions } from '@wix/web-methods';

export const multiply = webMethod(
  Permissions.SiteMember,
  (a: number, b: number) => a * b,
);
```

Enforce the same permissions in an HTTP endpoint as follows:

```ts
import type { APIRoute } from '@wix/custom-extensions/types';
import { auth } from '@wix/essentials';

export const POST: APIRoute = async ({ request }) => {
  const tokenInfo = await auth.getTokenInfo();

  // Restrict to site members (same intent as `Permissions.SiteMember` on the web method).
  if (tokenInfo.subjectType !== 'MEMBER') {
    return new Response('Forbidden', { status: 403 });
  }

  // ...handler logic
};
```

## Update your frontend call sites as you migrate each extension

The steps above move your backend code. Your frontend call sites stay in the legacy project until you migrate the extension that holds them, so there's nothing to change here yet.

Come back to this section from [Add the extension to the new project](https://dev.wix.com/docs/build-apps/develop-your-app/develop-an-app-with-the-cli/migrate-an-app-to-the-new-cli/migrate-an-app-from-the-legacy-cli/migrate-a-legacy-cli-app-to-the-new-wix-cli.md#add-the-extension-to-the-new-project) in the main migration guide, once the extension's code is in the new project. Then replace its web method imports with HTTP calls.

For example, the web method is called from the frontend as follows:

```ts
import { multiply } from '<relative-path>/multiply.web';

const result = await multiply(6, 7);
```

Call an HTTP endpoint from the frontend as follows. A web method call returns a typed value and throws on failure, while an HTTP call returns a `Response`, so check `res.ok` and type the parsed body yourself:

```ts
import { httpClient } from '@wix/essentials';

const baseApiUrl = new URL(import.meta.url).origin;
const res = await httpClient.fetchWithAuth(`${baseApiUrl}/multiply`, {
  method: 'POST',
  body: JSON.stringify({ a: 6, b: 7 }),
});
if (!res.ok) {
  throw new Error(`Request failed with status ${res.status}`);
}
const { result } = (await res.json()) as { result: number };
```

## Full example

The following is a full example of migrating web methods to HTTP endpoints on the backend and frontend.

### Backend example

This example shows the backend implementation of a web method in your legacy project's `src/backend/` directory:

```ts
import { webMethod, Permissions } from '@wix/web-methods';

export const multiply = webMethod(
  // Restrict to site members
  Permissions.SiteMember,
  (a: number, b: number) => a * b,
);
```

The equivalent HTTP endpoint is in your new Wix CLI project's `src/api/` directory:

```ts
import type { APIRoute } from '@wix/custom-extensions/types';
import { auth } from '@wix/essentials';

export const POST: APIRoute = async ({ request }) => {
  const tokenInfo = await auth.getTokenInfo();

  // Restrict to site members (same intent as `Permissions.SiteMember` on the web method).
  if (tokenInfo.subjectType !== 'MEMBER') {
    return new Response('Forbidden', { status: 403 });
  }

  const { a, b } = await request.json();
  return new Response(JSON.stringify({ result: a * b }));
};
```

### Frontend example

This example shows a web method called from the frontend:

```ts
import { multiply } from '<relative-path>/multiply.web';

const result = await multiply(6, 7);
```

The equivalent HTTP endpoint is called from the frontend as follows:

```ts
import { httpClient } from '@wix/essentials';

const baseApiUrl = new URL(import.meta.url).origin;
const res = await httpClient.fetchWithAuth(`${baseApiUrl}/multiply`, {
  method: 'POST',
  body: JSON.stringify({ a: 6, b: 7 }),
});
if (!res.ok) {
  throw new Error(`Request failed with status ${res.status}`);
}
const { result } = (await res.json()) as { result: number };
```