# Managing APIs

Defining custom HTTP-style endpoints on SurrealDB so clients hit a narrow surface instead of arbitrary queries.

Custom APIs let you expose a small, deliberate set of routes on top of SurrealDB. Instead of handing every caller a generic query channel, you can define named endpoints that return a familiar HTTP-shaped result.

That pattern pairs well with tightening access such as varying querying timeouts for free or entry-level users on an app. To accomplish this, you can combine [`DEFINE API`](/docs/reference/query-language/statements/define/api.md) with [capabilities](/docs/learn/security/authorization/capabilities.md) or server [environment variables](/docs/reference/cli/surrealdb-cli/environment-variables.md) so certain users only see the APIs you designed.

## Where endpoints live

Each definition maps to a path under `/api/:namespace/:database/...`, followed by the path you gave in the statement. As such, an endpoint path like `get_users` in namespace `my_namespace` and database `my_database` becomes something like `/api/my_namespace/my_database/get_users` over HTTP.

## Reading the incoming request

Inside the handler you can use the built-in [`$request`](/docs/reference/query-language/language-primitives/parameters.md#request) value. This parameter holds the `method`, `body`, `headers`, `query`, `params` (from your path pattern), and `context` you or middleware may have set.

## A minimal endpoint

Here is a small endpoint that echoes part of the body and sets a couple of headers. The precise clauses and permissions are spelled out in the [`DEFINE API`](/docs/reference/query-language/statements/define/api.md) reference page.

```surql title="Defining an API endpoint"
DEFINE API "/test"
    FOR get, post
        MIDDLEWARE
            api::timeout(1s)
        THEN {
            {
                status: 200,
                body: {
                    request: $request.body,
                    response: "The server works"
                },
                headers: {
                    'last-modified': <string>time::now(),
                    'expires': <string>(time::now() + 4d)
                }
            };
        };
```

You can test the endpoint from SurrealQL without needing to deploy by calling the [`api::invoke`](/docs/reference/query-language/functions/database-functions/api.md) function. This function takes either the path alone or the path plus a body object.

```surql
api::invoke("/test");

api::invoke("/test", {
    body: {
        hi: "please",
        give: "me",
        the: "information"
    }
});
```

## Path patterns: one segment or the rest of the URL

The path string in `DEFINE API` can be static, or it can capture pieces of the URL.

A segment like `"/users/:id"` binds one path component - anything in that slot shows up on `$request.params`. A trailing pattern with `*` instead of `:` matches everything from that point - handy for nested paths or file-like routes.

```surql
DEFINE API OVERWRITE "/test/:anything_goes" FOR get THEN {
    RETURN {
        body: {
            some: "data"
        }
    }
};

api::invoke("/test/this_matches");
api::invoke("/test/same_here");
api::invoke("/test/but/this/wont/match");
```

Here the first two calls hit the handler; the third does not, because `:anything_goes` only covers a single segment - extra slashes mean “no matching route”, which surfaces as a 404-style result from `api::invoke`.

To accept multiple trailing segments, switch the capture to the `*` form:

```surql
DEFINE API OVERWRITE "/test/*anything_goes" FOR get THEN {
    RETURN {
        body: {
            some: "data"
        }
    }
};

api::invoke("/test/this_matches");
api::invoke("/test/same_here");
api::invoke("/test/works/with/multiple/paths/now");
```

All three calls succeed because the remainder of the path is treated as one captured piece.

## Middleware

Built-in helpers such as `api::timeout` sit in the `MIDDLEWARE` list before your `THEN` block. For more details on how to use middleware, see the [next page](/docs/learn/querying/custom-apis/middleware.md).

## Where to read more

* [`DEFINE API`](/docs/reference/query-language/statements/define/api.md) - full statement reference, including `PERMISSIONS`.
* [API functions](/docs/reference/query-language/functions/database-functions/api.md) - `api::invoke` and related helpers.
* [Custom functions](/docs/learn/querying/concepts-and-guides/custom-functions.md) - how `fn::` functions fit into larger designs.
