Custom API Endpoints: streamlining your architecture

Published: (February 25, 2026 at 12:53 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

![Cover image for Custom API Endpoints: streamlining your architecture](https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbxyduw7a0t2nc23j0hj.png)

[![SurrealDB profile image](https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4795%2F6422ffbe-ae06-43aa-8dc1-bfb088408cc0.png)](https://dev.to/surrealdb)
![Mark Gyles](https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1602559%2Ff82af600-3936-4f4a-a067-dfee7a3228d4.jpg)

# Custom API Endpoints: streamlining your architecture

With the release of **SurrealDB 3.0**, we’re excited to announce the stabilisation of a powerful feature: [`DEFINE API`](https://surrealdb.com/docs/surrealql/statements/define/api). This innovative addition allows developers to define database behaviours, set up middleware, and create custom API endpoints directly within the familiar SurrealQL query language.

Traditionally, applications connect to databases through middleware, resulting in a three‑layer architecture:

Client → Middleware (API) → Database


With SurrealDB's new `DEFINE API`, you can simplify your infrastructure significantly:

Client → Database


This streamlined approach not only reduces complexity but also enhances performance and ease of management.

---

## Real‑world use case: implementing rate limits for a social app

Consider a scenario: you’ve built a popular social application that allows users to see recent comments. You want to provide a free API for anonymous users, but you also need to ensure these guest users don’t overload your system resources during peak traffic.

Below is a step‑by‑step guide to achieving this efficiently using SurrealDB’s `DEFINE API`.

### 1. Setup – restrict arbitrary queries

Start the SurrealDB server with the `--deny‑arbitrary‑query` flag (and enable the experimental `define_api` feature):

```bash
surreal start \
  --user root \
  --pass root \
  --deny-arbitrary-query guest \
  --allow-experimental define_api

This prevents certain groups (e.g., guest or record users) from sending arbitrary queries. Guest users will have to use the API endpoint instead.

You can then connect with Surrealist or via the CLI:

surreal sql --user root --pass root

2. Define your API endpoint

Add a DEFINE API statement. The endpoint will be reachable at /get_latest and will only accept GET requests:

DEFINE API OVERWRITE "/get_latest" FOR get

3. Add middleware functions

Attach middleware using SurrealDB’s built‑in API functions. Here we give guest users up to 50 ms of server time:

MIDDLEWARE
    api::timeout(50ms)

4. Define the return data

Finish the statement by specifying what should be returned – a SELECT of all comment records created in the last ten minutes, ordered by newest first:

THEN {
    {
        body: SELECT * FROM comment:[time::now()-10m].. ORDER BY id DESC
    }
};

5. Full DEFINE API statement

Putting it all together:

DEFINE API OVERWRITE "/get_latest" FOR get
    MIDDLEWARE
        api::timeout(50ms),
    THEN {
        {
            body: SELECT * FROM comment:[time::now()-10m].. ORDER BY id DESC
        }
    };

6. Testing your endpoint

From SurrealQL

Create a couple of comments and invoke the endpoint:

CREATE comment:[time::now()] SET user_says = "Nice blog!";
CREATE comment:[time::now()] SET user_says = "Can't wait for the new version";

api::invoke("/get_latest");

Via HTTP

The endpoint is exposed at:

http://localhost:8000/api///get_latest

For a namespace test_ns and a database test_db:

curl -H "Accept: application/json" \
  http://localhost:8000/api/test_ns/test_db/get_latest

Expected output

[
  {"id":"comment:[d'2026-02-12T01:10:02.445607Z']","user_says":"Can't wait for the new version"},
  {"id":"comment:[d'2026-02-12T01:08:33.709073Z']","user_says":"Nice blog!"}
]

Extending middleware

The MIDDLEWARE clause can also accept custom middleware defined with a regular DEFINE FUNCTION statement. These functions automatically receive the user request and can inspect or modify the response as it is built.

For more examples and advanced usage, refer to the official documentation.

Custom API with Arguments

SurrealDB lets you define custom API endpoints directly in the database, allowing you to add middleware and manipulate responses on‑the‑fly. Below is a concise example that demonstrates how to create an API endpoint with a middleware function that adds a prefix to the response body.

DEFINE FUNCTION fn::add_prefix($req: object, $next: function, $prefix: string) -> object {
    LET $res = $next($req);
    LET $res = $res + {
        body: $res.body + {
            prefix: $prefix + ": " + $res.body.message
        }
    };
    $res;
};

DEFINE API "/custom_with_args"
    FOR get
        MIDDLEWARE
            fn::add_prefix("PREFIX")
        THEN {
            {
                status: 200,
                body: {
                    message: "original message"
                }
            };
        };

Note: With DEFINE API, SurrealDB brings the power of API design directly into the database—no external layers, no additional frameworks. Whether you’re enforcing limits, defining access, or building powerful custom endpoints, this feature helps you simplify your architecture and move faster.


Explore Further

Dive deeper into SurrealDB’s powerful API management capabilities:

Harness the power of DEFINE API and redefine what’s possible with SurrealDB 3.0.

0 views
Back to Blog

Related posts

Read more »