Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Your first API in 10 minutes

Every new admin or developer account of API Maker comes with a sample shop : an instance named mongodb, a database shop with the schemas of categories, customers, products, orders and orderItems, custom APIs under /default/…, hooks, an event, a migration script and a utility class. This page uses it, so you only need a MongoDB to point it at.

Nothing installed yet ?

Install on a server with one command, or run API Maker on your computer with the desktop app. Both print the URL of the admin panel and the sign-in details. The defaults of the install script are [email protected] / Admin_123456789.

In the commands below, $AM is the URL of your API Maker (http://127.0.0.1:38246, or the domain of your server) and admin is the user path of the account.

1. Point the sample instance at a MongoDB

  1. Open the default secret Admin panel → API Security → Secret Management → the secret named Default. It is TypeScript : a common object with keys, connection strings and the passwords of API users.
  2. Set common.connectionString.mongodb The sample instance reads its connection string from that path. Paste the string of any MongoDB you can reach (the MongoDB of API Maker Local Run, a MongoDB Atlas cluster, the one the install script created : it is in /root/config/.env). Save.
    connectionString: {
        mongodb: 'mongodb://user:[email protected]:27017/?authSource=admin&replicaSet=rs0&directConnection=true',
        // ...
    },
  3. Check the instance API Info → Instance API (DB API) → instance mongodb → Test Instance. The database shop appears in the list after the first save below : MongoDB creates databases and collections on first write.

2. Get a token

The account has an API user named default. Its password is the value of common.apiUserPasswords.default in the secret : 12345 until you change it.

curl -s -X POST "$AM/api/system-api/admin/token" \
  -H "Content-Type: application/json" \
  -d '{ "u": "default", "p": "12345" }'
{
    "success": true,
    "statusCode": 200,
    "data": { "token": "eyJhbGciOi…", "refresh_token": "eyJhbGciOi…", "expires_in": 259200 }
}

Keep the token in a variable : TOKEN=eyJhbGciOi…. Every call below sends it in x-am-authorization. The group Default of this API user allows every API, which is fine for a first try and wrong for production : see Handle role based permissions.

3. Save a category, then a product

The product schema needs a category (a relation to categories._id), a currency from IN, US or GB, and a price_cents. So the category comes first.

curl -s -X POST "$AM/api/schema/admin/mongodb/shop/categories/save-single-or-multiple" \
  -H "Content-Type: application/json" -H "x-am-authorization: $TOKEN" \
  -d '{ "name": "Accessories" }'
{
    "success": true,
    "statusCode": 201,
    "data": { "_id": "66fa1c2d9b1e4a0012a3c001", "name": "Accessories", "slug": "accessories" }
}

The _id was generated by API Maker (isAutoGenerateByAM) and the slug by the conversionFun of the schema. Now the product :

curl -s -X POST "$AM/api/schema/admin/mongodb/shop/products/save-single-or-multiple" \
  -H "Content-Type: application/json" -H "x-am-authorization: $TOKEN" \
  -d '{ "name": "  Wireless Mouse ", "currency": "in", "price_cents": 129900, "category": "66fa1c2d9b1e4a0012a3c001" }'
{
    "success": true,
    "statusCode": 201,
    "data": {
        "_id": "66fa1c9a9b1e4a0012a3c002",
        "public_id": "01J8Z3C9F2X4Q7N8V1M6K5R0TA",
        "product_no": 1000,
        "name": "Wireless Mouse",
        "slug": "wireless-mouse",
        "status": "DRAFT",
        "currency": "IN",
        "price_cents": 129900,
        "is_active": true,
        "created_at": "2026-09-30T10:15:22.418Z",
        "updated_at": "2026-09-30T10:15:22.418Z",
        "category": "66fa1c2d9b1e4a0012a3c001"
    }
}

Look at what the schema did : the name was trimmed, currency upper-cased, status and is_active got their defaults, created_at its default function, product_no the next number of its auto increment, public_id a ULID, slug a value computed from the name. That is the schema pipeline ; the generated APIs under /api/gen would have stored the body as it came.

4. Read it back

Get all products, only some fields, with the category populated from its relation :

curl -s "$AM/api/schema/admin/mongodb/shop/products?select=name,slug,price_cents,category&deep=[{s_key:'category'}]" \
  -H "x-am-authorization: $TOKEN"
{
    "success": true,
    "statusCode": 200,
    "data": [
        {
            "_id": "66fa1c9a9b1e4a0012a3c002",
            "name": "Wireless Mouse",
            "slug": "wireless-mouse",
            "price_cents": 129900,
            "category": { "_id": "66fa1c2d9b1e4a0012a3c001", "name": "Accessories", "slug": "accessories" }
        }
    ]
}

deep only needed the source key : the target table and key come from the schema. The same filters work as query params on get all, or in the body of the query API :

curl -s -X POST "$AM/api/schema/admin/mongodb/shop/products/query" \
  -H "Content-Type: application/json" -H "x-am-authorization: $TOKEN" \
  -d '{ "find": { "status": "DRAFT", "price_cents": { "$gte": 100000 } }, "sort": "-created_at", "limit": 10, "getTotalCount": true }'

5. See a refusal

Send a product that breaks three rules at once :

curl -s -X POST "$AM/api/schema/admin/mongodb/shop/products/save-single-or-multiple" \
  -H "Content-Type: application/json" -H "x-am-authorization: $TOKEN" \
  -d '{ "name": "M", "currency": "EUR", "price_cents": -5, "category": "66fa1c2d9b1e4a0012a3c001", "discount_pct": 10 }'
{
    "success": false,
    "statusCode": 400,
    "errors": [
        { "type": "minLength", "field": "name", "message": "Property 'name' should have minimum length of '2'.", "code": 400 },
        { "type": "enumValidation", "field": "currency", "message": "Property 'currency' should have any value from [IN, US, GB].", "code": 400 },
        { "type": "min", "field": "price_cents", "message": "Please provide minimum '0' for 'price_cents' field.", "code": 400 },
        { "type": "invalidValue", "field": "discount_pct", "message": "A discount requires approved_by.", "code": 400 }
    ]
}

Every problem is reported in one reply, including the message thrown by the validatorFun of discount_pct, and nothing was saved. The messages can be translated per caller with internationalization.

6. Try the rest of the sample

  • API testing page (API Info → API Testing) : pick the instance, the table and the API, get a sample payload, send it and read the response, then copy the call as code in 21 languages.
  • Custom APIs under API Info → Custom API : /default/deep-populate-orders, /default/find-join-products, /default/caching-example, /default/ws-notify, /default/login… Open one to read a complete, commented example of the feature. They have the access type NO_ACCESS, so they run from the testing page and from other code, not from outside, except the login and captcha ones.
  • Hooks : the instance, database and collection hooks of the sample show how a person is kept to their own rows.
  • Schemas of the five collections show every schema feature in use.

Where to go next