wix-cli-backend-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Wix Backend API Builder

Wix Backend API 构建工具

Creates Astro server endpoint extensions for Wix CLI applications. Backend APIs are server-side routes that handle HTTP requests, process data, and return responses. They are automatically discovered and don't require extension registration. Endpoints are generated based on API specifications and are designed to integrate well with frontend applications in fullstack applications.
为Wix CLI应用创建Astro服务器端点扩展。后端API是处理HTTP请求、处理数据并返回响应的服务器端路由。它们会被自动发现,无需进行扩展注册。端点基于API规范生成,旨在与全栈应用中的前端应用良好集成。

Architecture

架构

Astro server endpoints are TypeScript files (
.ts
extension) in the
src/pages/api/
directory that export named functions for HTTP methods. They are automatically converted to API routes.
Key characteristics:
  • Files use the
    .ts
    extension
  • Files export named functions:
    GET
    ,
    POST
    ,
    PUT
    ,
    DELETE
    ,
    PATCH
  • Always import
    APIRoute
    type from 'astro' for proper typing
  • Always return
    Response
    objects
  • Use async/await for asynchronous operations
  • No extension registration needed (auto-discovered)
Astro服务器端点是
src/pages/api/
目录下的TypeScript文件(
.ts
扩展名),导出HTTP方法对应的命名函数。它们会被自动转换为API路由。
核心特性:
  • 文件使用
    .ts
    扩展名
  • 文件导出命名函数:
    GET
    POST
    PUT
    DELETE
    PATCH
  • 必须从'astro'导入
    APIRoute
    类型以获得正确的类型提示
  • 始终返回
    Response
    对象
  • 异步操作使用async/await
  • 无需扩展注册(自动发现)

File Structure and Naming

文件结构与命名

Basic Endpoint

基础端点

File path determines the endpoint URL:
src/pages/api/users.ts → /api/users
文件路径决定端点URL:
src/pages/api/users.ts → /api/users

Dynamic Routes

动态路由

Use square brackets for dynamic parameters:
src/pages/api/users/[id].ts → /api/users/:id
src/pages/api/posts/[slug].ts → /api/posts/:slug
src/pages/api/users/[userId]/posts/[postId].ts → /api/users/:userId/posts/:postId
使用方括号定义动态参数:
src/pages/api/users/[id].ts → /api/users/:id
src/pages/api/posts/[slug].ts → /api/posts/:slug
src/pages/api/users/[userId]/posts/[postId].ts → /api/users/:userId/posts/:postId

HTTP Methods

HTTP方法

Export named functions for each HTTP method you want to support. Always import the
APIRoute
type from 'astro'
for proper typing:
typescript
import type { APIRoute } from "astro";

export async function GET({ params, request }) {
  // Handle GET request
}

export async function POST({ params, request }) {
  // Handle POST request
}

export async function PUT({ params, request }) {
  // Handle PUT request
}

export async function DELETE({ params, request }) {
  // Handle DELETE request
}

export async function PATCH({ params, request }) {
  // Handle PATCH request
}
为每个需要支持的HTTP方法导出命名函数。必须从'astro'导入
APIRoute
类型
以获得正确的类型提示:
typescript
import type { APIRoute } from "astro";

export async function GET({ params, request }) {
  // Handle GET request
}

export async function POST({ params, request }) {
  // Handle POST request
}

export async function PUT({ params, request }) {
  // Handle PUT request
}

export async function DELETE({ params, request }) {
  // Handle DELETE request
}

export async function PATCH({ params, request }) {
  // Handle PATCH request
}

Request Handling

请求处理

Path Parameters

路径参数

Extract dynamic route parameters from
params
:
typescript
export async function GET({ params }) {
  const { id } = params; // From /api/users/[id]

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  // Use id to fetch data
}
params
中提取动态路由参数:
typescript
export async function GET({ params }) {
  const { id } = params; // From /api/users/[id]

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  // Use id to fetch data
}

Query Parameters

查询参数

Extract query string parameters from
request.url
using
new URL(request.url).searchParams
:
typescript
export async function GET({ request }) {
  const url = new URL(request.url);
  const search = url.searchParams.get("search");
  const limit = parseInt(url.searchParams.get("limit") || "10", 10);
  const offset = parseInt(url.searchParams.get("offset") || "0", 10);

  // Use query parameters
}
Note: Use
request.url
to access the full URL, and
new URL(request.url).searchParams
for query parameters.
使用
new URL(request.url).searchParams
request.url
中提取查询字符串参数:
typescript
export async function GET({ request }) {
  const url = new URL(request.url);
  const search = url.searchParams.get("search");
  const limit = parseInt(url.searchParams.get("limit") || "10", 10);
  const offset = parseInt(url.searchParams.get("offset") || "0", 10);

  // Use query parameters
}
注意: 使用
request.url
访问完整URL,使用
new URL(request.url).searchParams
获取查询参数。

Request Body

请求体

Parse JSON body from POST/PUT requests:
typescript
export async function POST({ request }) {
  try {
    const body = await request.json();
    const { title, content } = body;

    // Validate required fields
    if (!title || !content) {
      return new Response(
        JSON.stringify({ error: "Title and content required" }),
        {
          status: 400,
          statusText: "Bad Request",
          headers: { "Content-Type": "application/json" },
        }
      );
    }

    // Process data
  } catch (error) {
    return new Response(JSON.stringify({ error: "Invalid JSON" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }
}
解析POST/PUT请求的JSON体:
typescript
export async function POST({ request }) {
  try {
    const body = await request.json();
    const { title, content } = body;

    // Validate required fields
    if (!title || !content) {
      return new Response(
        JSON.stringify({ error: "Title and content required" }),
        {
          status: 400,
          statusText: "Bad Request",
          headers: { "Content-Type": "application/json" },
        }
      );
    }

    // Process data
  } catch (error) {
    return new Response(JSON.stringify({ error: "Invalid JSON" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }
}

Headers

请求头

Access request headers:
typescript
export async function GET({ request }) {
  const authHeader = request.headers.get("Authorization");
  const contentType = request.headers.get("Content-Type");

  // Use headers
}
访问请求头:
typescript
export async function GET({ request }) {
  const authHeader = request.headers.get("Authorization");
  const contentType = request.headers.get("Content-Type");

  // Use headers
}

Response Patterns

响应模式

Always return a
Response
object with appropriate status codes and headers. Use
JSON.stringify()
for JSON responses:
始终返回带有适当状态码和请求头的
Response
对象。JSON响应使用
JSON.stringify()
处理:

Success Response

成功响应

typescript
return new Response(JSON.stringify({ data: result }), {
  status: 200,
  headers: {
    "Content-Type": "application/json",
  },
});
typescript
return new Response(JSON.stringify({ data: result }), {
  status: 200,
  headers: {
    "Content-Type": "application/json",
  },
});

Created Response

创建成功响应

typescript
return new Response(JSON.stringify({ id: newId, ...data }), {
  status: 201,
  headers: {
    "Content-Type": "application/json",
  },
});
typescript
return new Response(JSON.stringify({ id: newId, ...data }), {
  status: 201,
  headers: {
    "Content-Type": "application/json",
  },
});

Error Responses

错误响应

typescript
// Bad Request (400)
return new Response(JSON.stringify({ error: "Invalid input" }), {
  status: 400,
  statusText: "Bad Request",
  headers: { "Content-Type": "application/json" },
});

// Not Found (404)
return new Response(JSON.stringify({ error: "Resource not found" }), {
  status: 404,
  statusText: "Not Found",
  headers: { "Content-Type": "application/json" },
});

// Internal Server Error (500)
return new Response(JSON.stringify({ error: "Internal server error" }), {
  status: 500,
  statusText: "Internal Server Error",
  headers: { "Content-Type": "application/json" },
});
typescript
// Bad Request (400)
return new Response(JSON.stringify({ error: "Invalid input" }), {
  status: 400,
  statusText: "Bad Request",
  headers: { "Content-Type": "application/json" },
});

// Not Found (404)
return new Response(JSON.stringify({ error: "Resource not found" }), {
  status: 404,
  statusText: "Not Found",
  headers: { "Content-Type": "application/json" },
});

// Internal Server Error (500)
return new Response(JSON.stringify({ error: "Internal server error" }), {
  status: 500,
  statusText: "Internal Server Error",
  headers: { "Content-Type": "application/json" },
});

Wix Data Integration

Wix Data 集成

Use
@wix/data
SDK to interact with Wix Data collections:
typescript
import { items } from "@wix/data";

export async function GET({ params }) {
  const { id } = params;

  try {
    if (id) {
      // Get single item
      const item = await items.get("collectionId", id);
      return new Response(JSON.stringify(item), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    } else {
      // Get all items
      const results = await items.query("collectionId").limit(50).find();

      return new Response(JSON.stringify(results.items), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    }
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to fetch data" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function POST({ request }) {
  try {
    const body = await request.json();

    const newItem = await items.insert("collectionId", body);

    return new Response(JSON.stringify(newItem), {
      status: 201,
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to create item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function PUT({ params, request }) {
  const { id } = params;

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    const body = await request.json();

    const updatedItem = await items.update("collectionId", {
      _id: id,
      ...body,
    });

    return new Response(JSON.stringify(updatedItem), {
      status: 200,
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to update item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function DELETE({ params }) {
  const { id } = params;

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    await items.remove("collectionId", id);

    return new Response(null, {
      status: 204,
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to delete item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}
Query methods:
eq
,
ne
,
gt
,
ge
,
lt
,
le
,
between
,
contains
,
startsWith
,
endsWith
,
hasSome
,
hasAll
,
isEmpty
,
isNotEmpty
,
and
,
or
,
not
,
ascending
,
descending
,
limit
,
skip
,
include
使用
@wix/data
SDK与Wix Data集合交互:
typescript
import { items } from "@wix/data";

export async function GET({ params }) {
  const { id } = params;

  try {
    if (id) {
      // Get single item
      const item = await items.get("collectionId", id);
      return new Response(JSON.stringify(item), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    } else {
      // Get all items
      const results = await items.query("collectionId").limit(50).find();

      return new Response(JSON.stringify(results.items), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    }
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to fetch data" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function POST({ request }) {
  try {
    const body = await request.json();

    const newItem = await items.insert("collectionId", body);

    return new Response(JSON.stringify(newItem), {
      status: 201,
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to create item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function PUT({ params, request }) {
  const { id } = params;

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    const body = await request.json();

    const updatedItem = await items.update("collectionId", {
      _id: id,
      ...body,
    });

    return new Response(JSON.stringify(updatedItem), {
      status: 200,
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to update item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

export async function DELETE({ params }) {
  const { id } = params;

  if (!id) {
    return new Response(JSON.stringify({ error: "ID required" }), {
      status: 400,
      statusText: "Bad Request",
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    await items.remove("collectionId", id);

    return new Response(null, {
      status: 204,
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: "Failed to delete item" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}
查询方法:
eq
,
ne
,
gt
,
ge
,
lt
,
le
,
between
,
contains
,
startsWith
,
endsWith
,
hasSome
,
hasAll
,
isEmpty
,
isNotEmpty
,
and
,
or
,
not
,
ascending
,
descending
,
limit
,
skip
,
include

Output Structure

输出结构

src/pages/api/
├── users.ts              # /api/users endpoint
├── users/
│   └── [id].ts          # /api/users/:id endpoint
└── posts.ts             # /api/posts endpoint
src/pages/api/
├── users.ts              # /api/users endpoint
├── users/
│   └── [id].ts          # /api/users/:id endpoint
└── posts.ts             # /api/posts endpoint

Examples

示例

For additional examples, see references/backend-api-examples.md.
更多示例请参考references/backend-api-examples.md

Extension Registration

扩展注册

Backend API endpoints do NOT require extension registration. They are automatically discovered from the
src/pages/api/
directory structure.
后端API端点无需进行扩展注册。 它们会从
src/pages/api/
目录结构中自动被发现。

Verification

验证

After implementation, use wix-cli-app-validation to validate TypeScript compilation, build, preview, and runtime behavior.
实现完成后,使用wix-cli-app-validation验证TypeScript编译、构建、预览和运行时行为。

Code Quality Requirements

代码质量要求

  • Strict TypeScript (no
    any
    , explicit return types)
  • Import
    APIRoute
    type from 'astro' for proper typing
  • Always return
    Response
    objects
  • Use
    JSON.stringify()
    for JSON responses
  • Use proper HTTP status codes (200, 201, 400, 404, 500, etc.)
  • Include proper headers (
    Content-Type: application/json
    )
  • Include
    statusText
    in error responses for clarity
  • Handle errors gracefully with try/catch blocks
  • Validate input parameters and request bodies
  • Use async/await for asynchronous operations
  • Keep endpoints focused and single-purpose
  • No
    @ts-ignore
    comments
  • 严格TypeScript(禁止使用
    any
    ,明确返回类型)
  • 从'astro'导入
    APIRoute
    类型以获得正确的类型提示
  • 始终返回
    Response
    对象
  • JSON响应使用
    JSON.stringify()
    处理
  • 使用正确的HTTP状态码(200、201、400、404、500等)
  • 包含正确的请求头(
    Content-Type: application/json
  • 错误响应中包含
    statusText
    以提升清晰度
  • 使用try/catch块优雅处理错误
  • 验证输入参数和请求体
  • 异步操作使用async/await
  • 保持端点聚焦,单一职责
  • 禁止使用
    @ts-ignore
    注释