Wix Backend API Builder
Creates HTTP endpoints for Wix CLI applications — server-side routes that handle HTTP requests, process data, and return responses. HTTP endpoints are powered by Astro endpoints and are automatically discovered from the file system.
Key facts:
-
Files live in src/pages/api/ with .ts extension
-
Cannot be added via npm run generate — create files directly
-
Don't appear on the Extensions page in the app dashboard
-
No extension registration needed (auto-discovered)
-
Replace the legacy "HTTP functions" from the previous Wix CLI for Apps
Use Cases
Use HTTP endpoints when you need to:
-
Build REST APIs with multiple HTTP methods
-
Integrate with external APIs or services
-
Handle complex form submissions or file uploads
-
Serve dynamic content (images, RSS feeds, personalized data)
-
Access runtime data or server-side databases
File Structure and Naming
Basic Endpoint
File path determines the endpoint URL:
src/pages/api/<your-endpoint-name>.ts
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
HTTP Methods
Export named functions for each HTTP method. Type with APIRoute from astro . Each handler receives a request object and returns a Response :
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ request }) => { console.log("Log from GET."); // This message logs to your CLI. return new Response("Response from GET."); // This response is visible in the browser console };
export const POST: APIRoute = async ({ request }) => { const data = await request.json(); console.log("Log POST with body: ", data); // This message logs to your CLI. return new Response(JSON.stringify(data)); // This response is visible in the browser console. };
Request Handling
Path Parameters
export const GET: APIRoute = async ({ 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
Use new URL(request.url).searchParams :
export const GET: APIRoute = async ({ 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 Body
Parse JSON body from POST/PUT/PATCH requests:
export const POST: APIRoute = async ({ request }) => { try { const body = await request.json(); const { title, content } = body;
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 { return new Response(JSON.stringify({ error: "Invalid JSON" }), { status: 400, statusText: "Bad Request", headers: { "Content-Type": "application/json" }, }); } };
Headers
const authHeader = request.headers.get("Authorization"); const contentType = request.headers.get("Content-Type");
Response Patterns
Always return a Response object with proper status codes and headers:
// 200 OK return new Response(JSON.stringify({ data: result }), { status: 200, headers: { "Content-Type": "application/json" }, });
// 201 Created return new Response(JSON.stringify({ id: newId, ...data }), { status: 201, headers: { "Content-Type": "application/json" }, });
// 204 No Content (for DELETE) return new Response(null, { status: 204 });
// 400 Bad Request return new Response(JSON.stringify({ error: "Invalid input" }), { status: 400, statusText: "Bad Request", headers: { "Content-Type": "application/json" }, });
// 404 Not Found return new Response(JSON.stringify({ error: "Not found" }), { status: 404, statusText: "Not Found", headers: { "Content-Type": "application/json" }, });
// 500 Internal Server Error return new Response(JSON.stringify({ error: "Internal server error" }), { status: 500, statusText: "Internal Server Error", headers: { "Content-Type": "application/json" }, });
Frontend Integration
Call HTTP endpoints from frontend components using Wix's built-in HTTP client (httpClient.fetchWithAuth() ):
import { httpClient } from "@wix/essentials";
// GET request
const baseApiUrl = new URL(import.meta.url).origin;
const res = await httpClient.fetchWithAuth(
${baseApiUrl}/api/<your-endpoint-name>,
);
const data = await res.text();
// POST request
const res = await httpClient.fetchWithAuth(
${baseApiUrl}/api/<your-endpoint-name>,
{
method: "POST",
body: JSON.stringify({ message: "Hello from frontend" }),
},
);
const data = await res.json();
Build, Deploy, and Delete
To take HTTP endpoints to production, build and release your project:
-
Build the project assets using the build command.
-
Optionally create preview URLs using the preview command to share with team members for testing.
-
Release your project using the release command.
Once released, endpoints are accessible at production URLs and handle live traffic.
To delete an HTTP endpoint, remove the file under src/pages/api/ and release again.
Output Structure
src/pages/api/ ├── users.ts # /api/users endpoint ├── users/ │ └── [id].ts # /api/users/:id endpoint └── posts.ts # /api/posts endpoint
Code Quality Requirements
-
Strict TypeScript (no any , explicit return types)
-
Type all handlers with APIRoute from astro
-
Always return Response objects with JSON.stringify() for JSON
-
Proper HTTP status codes (200, 201, 204, 400, 404, 500)
-
Include Content-Type: application/json header on JSON responses
-
Include statusText in error responses
-
Handle errors with try/catch blocks
-
Validate input parameters and request bodies
-
Use async/await for asynchronous operations
-
No @ts-ignore comments