# Your First API in 10 Minutes

> A hands-on start with the sample shop every new API Maker account has - connect MongoDB, get a token, save a category and a product through the schema APIs, read them back with filters and deep populate, and see a validation error.

Source: https://docs.apimaker.dev/v1/docs/getting-started/first-api.html

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.

!!! tip "Nothing installed yet ?"
    [Install on a server](/v1/docs/getting-started/install-on-server.html) with one command, or [run API Maker on your computer](/v1/docs/getting-started/local-run.html) with the desktop app. Both print the URL of the admin panel and the sign-in details. The defaults of the install script are `admin@admin.com` / `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

<ol class="am-steps">
<li><strong>Open the default secret</strong> Admin panel → <b>API Security → Secret Management</b> → the secret named <code>Default</code>. It is TypeScript : a <code>common</code> object with keys, connection strings and the passwords of API users.</li>
<li><strong>Set <code>common.connectionString.mongodb</code></strong> 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 <code>/root/config/.env</code>). Save.
<pre><code class="language-typescript">connectionString: {
    mongodb: 'mongodb://user:password@127.0.0.1:27017/?authSource=admin&amp;replicaSet=rs0&amp;directConnection=true',
    // ...
},</code></pre></li>
<li><strong>Check the instance</strong> <b>API Info → Instance API (DB API)</b> → instance <code>mongodb</code> → <b>Test Instance</b>. The database <code>shop</code> appears in the list after the first save below : MongoDB creates databases and collections on first write.</li>
</ol>

## 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.

```bash
curl -s -X POST "$AM/api/system-api/admin/token" \
  -H "Content-Type: application/json" \
  -d '{ "u": "default", "p": "12345" }'
```

```json
{
    "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](/v1/docs/authorization/handle-role-based-permissions.html).

## 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.

```bash
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" }'
```

```json
{
    "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 :

```bash
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" }'
```

```json
{
    "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](/v1/docs/schema/schema.html) ; 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 :

```bash
curl -s "$AM/api/schema/admin/mongodb/shop/products?select=name,slug,price_cents,category&deep=[{s_key:'category'}]" \
  -H "x-am-authorization: $TOKEN"
```

```json
{
    "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 :

```bash
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 :

```bash
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 }'
```

```json
{
    "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](/v1/docs/i18/i18.html).

## 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

    <a class="am-card" href="/v1/docs/apis-all/overview.html">All APIs at a glanceEvery operation of a table, with its method and URL.</a>
    <a class="am-card am-card--green" href="/v1/docs/schema/schema.html">Table schemaWrite the schema of your own tables.</a>
    <a class="am-card am-card--purple" href="/v1/docs/apis-all/custom-apis/user-created-custom-api.html">Custom APIsYour first TypeScript function behind a URL.</a>
    <a class="am-card am-card--red" href="/v1/docs/authorization/handle-role-based-permissions.html">Role based permissionsReplace the Default group before going live.</a>
