> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fetchski-attract.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Find a document's id

> List every document with only the fields you need, and take the id you want.

```json 200 wrap theme={"dark"}
{
  "results": [
    { "_id": "ckq3v8x5b0001:en:published", "title": "Home", "slug": "/" },
    { "_id": "ckq3v8x5b0002:en:published", "title": "About Us", "slug": "/about-us" },
    { "_id": "ckq3v8x5b0003:en:published", "title": "Pricing", "slug": "/pricing" }
  ]
}
```

Most calls name one document by its id: get a page, change a post, publish a draft. The id comes from
a list, and a whole list of documents is big: every page with every field and every section is
easily hundreds of kilobytes. Ask the list for **only the fields you need** instead, and it answers
each document as its `_id` and those fields, nothing else.

> [card] [Name the fields](#name-the-fields)
>
> `fields=title,slug` on any list.

> [card] [Every document, in the SDKs](#every-document-in-the-sdks)
>
> `…Pick()` walks every page for you.

> [card] [Which id to send](#which-id-to-send)
>
> Published or draft, and why it matters.

## Name the fields

Every content type's list, and the page tree, take `fields`: the fields to answer beside each
document's `_id`.

**fields** `string[]`

`fields=title&fields=slug`, or `fields=title,slug`. At most 50. A field the type doesn't have is
refused with `400`, and the message lists the fields it has.

Every page on the site, in one flat list, each as its id, title and slug:

```bash Terminal theme={"dark"}
curl 'https://acme.example/api/v1/@apostrophecms/page?all=1&flat=1&fields=title,slug' \
  -H "Authorization: ApiKey $FETCHSKI_ATTRACT_KEY"
```

The same works on every list, with its own paging (`page`, `perPage`), search and filters:

```bash Terminal theme={"dark"}
curl 'https://acme.example/api/v1/@apostrophecms/blog?fields=title,slug&perPage=100' \
  -H "Authorization: ApiKey $FETCHSKI_ATTRACT_KEY"
```

> [accordion] The page tree keeps its shape
>
> Without `flat=1` the page tree answers the home page with its pages nested under `_children`.
> With `fields`, every page in the tree is trimmed the same way, and `_children` stays, so the tree
> is still a tree. In a flat list each page names its children by id: ask for `_children` to keep
> them.

> [accordion] Fields with an underscore
>
> Some fields are worked out when the document is read: `_url` (its address on the site) and the
> related documents (`_author`, `_category` …). Name them like any other field.

> [accordion] An API key without the type's read scope
>
> A key without the type's read scope reads only the fields the site makes public, and may name only
> those: any other is refused with `400`, listing the public ones. A key with the read scope may name
> any field ([Required scopes](https://docs.fetchski-attract.com/scopes.md)).

## Every document, in the SDKs

Every list method that takes `fields` has a `…Pick()` twin. It walks every page of the list for you,
100 documents a page, and answers one plain row per document: its `id`, then each field you named.

```php Laravel theme={"dark"}
use Fetchski\AttractCmsLaravel\Facades\Attract;

$pages = Attract::pages()->listPagesPick(fields: ['title', 'slug']);
// [['id' => 'ckq3v8x5b0002:en:published', 'title' => 'About Us', 'slug' => '/about-us'], …]
```

```ts TypeScript theme={"dark"}
const pages = await site.pages().listPagesPick(['title', 'slug']);
// [{ id: 'ckq3v8x5b0002:en:published', title: 'About Us', slug: '/about-us' }, …]
```

```bash Terminal theme={"dark"}
php artisan attract:call pages.listPagesPick fields=title,slug
```

The AdonisJS SDK is the TypeScript one: the same call, on the client your provider gives you.

> [card] Rows, not models
>
> A row holds only what you asked for, so it isn't the type's model: it's a plain array (Laravel) or
> object (TypeScript), with each value as the site sends it (a date is its ISO text).

> [card] The SDK's names
>
> Name a field as the SDK does (`url`) or as the site does (`_url`); the row always uses the SDK's
> name. A field the type doesn't have throws before anything is sent.

> [card] The list's own filters
>
> `…Pick()` takes its list's arguments, bar the page: `search`, `slug`, `aposMode`, `perPage` …

> [card] Fakes answer it too
>
> It records the list's own key, so a test fakes `blogPosts.listBlogs` and asserts that.

### Find one

> [tab] By its slug
>
> Every list but the page tree finds one document by its slug, so only that one comes back:
>
> ```php Laravel theme={"dark"}
> $post = Attract::blogPosts()->listBlogsPick(fields: ['title'], slug: 'spring-update')->first();
> // ['id' => …, 'title' => 'Spring update'], or null
> ```
>
> ```ts TypeScript theme={"dark"}
> const [post] = await site.blogPosts().listBlogsPick(['title'], { slug: 'spring-update' });
> ```

> [tab] A page
>
> Take the page from the list by its slug. In Laravel the list is lazy, so it stops reading at the
> page it finds:
>
> ```php Laravel theme={"dark"}
> $about = Attract::pages()->listPagesPick(fields: ['title', 'slug'])->firstWhere('slug', '/about-us');
> ```
>
> ```ts TypeScript theme={"dark"}
> const about = (await site.pages().listPagesPick(['title', 'slug'])).find(
>   (page) => page.slug === '/about-us'
> );
> ```

> [tab] By its text
>
> `search` matches documents by their text:
>
> ```php Laravel theme={"dark"}
> $matches = Attract::faqItems()->listFaqItemsPick(fields: ['title'], search: 'pricing');
> ```
>
> ```ts TypeScript theme={"dark"}
> const matches = await site.faqItems().listFaqItemsPick(['title'], { search: 'pricing' });
> ```

Then pass the row's `id` to the call that wants it:

```php Laravel theme={"dark"}
$page = Attract::pages()->getPage(id: $about['id']);
```

```ts TypeScript theme={"dark"}
const page = await site.pages().getPage({ id: about.id });
```

## Which id to send

An id names its document's locale and mode: `ckq3v8x5b0002:en:published` is the published copy,
`ckq3v8x5b0002:en:draft` its draft. A list answers the copies of the mode it reads, so pick the mode
you will work on:

> [card] To read what visitors see
>
> The default list answers published ids.

> [card] To change a draft
>
> List with `aposMode: 'draft'` (`aposMode=draft`): its ids are the drafts'. A draft read needs the
> type's write scope.

> [!TIP]
>
> The part before the first colon is the document's `aposDocId`, the same for both copies. A route that
> takes an id also takes the bare `aposDocId` with `aposMode`, which picks the copy for you. More in
> [Drafts and locales](https://docs.fetchski-attract.com/drafts-and-locales.md).

Every reference page of a method that takes an id shows its `…Pick()` call under **Find the id**.

## Related

> [card] [Lists and paging](https://docs.fetchski-attract.com/paging.md)
>
> How every list pages, and the `…All()` walks.

> [card] [Call from the terminal](https://docs.fetchski-attract.com/call-command.md)
>
> Any method, `…Pick()` included, with one Artisan command.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.