# Reading your data

> Evaluates one Osy# expression against your app and prints what it answered — the rows, the count, the projection. The read half of `osy run`, which only ever writes. It is not a query language: the expression goes through the same compiler your source does, so anything the language expresses is evaluable here.

<!-- id: local-reading-your-data · area: local · stability: stable · html: https://osysharp.com/reference/local/reading-your-data/ -->

## Summary        {#summary}
`osy run` puts data in. `osy query` gets it out.

```console
$ osy query "Job.OrderBy(j => j.Position)"
[
  { "Title": "re-plaster the hall", "Room": "hall", "Position": 10 },
  { "Title": "paint the shed",      "Room": "garden", "Position": 20 }
]

$ osy query "Job.Where(j => !j.Done).Count()"
2
```

## Signature      {#signature}
```console
osy query "<expression>"            // evaluate as ANONYMOUS — what a signed-out visitor sees
osy query "<expression>" --as ada@example.com   // …as one of your users — what THEY see
osy query "<expression>" --json     // the whole result envelope, for a script
```

## Description    {#description}

### It is not a query language   {#not-a-query-language}
The expression is lexed, parsed, resolved and run by **the same compiler your source goes through**. There is no
second grammar to learn and none to drift: whatever you could write inside a function body, you can write here.

That includes the refusals. A verb the language does not have comes back in the resolver's own words, with the
closed set it does have:

```console
$ osy query "Job.Nope(j => j.Title)"
✗ unknown query method 'Nope' — on 'Job' rows read from the data store the verbs are: All, Any, Average,
  Count, Distinct, … OrderBy, OrderByDescending, … Where
```

### What it answers with   {#shapes}
Whatever the expression evaluates to, rendered as JSON — because an expression has no single shape:

| you write | you get |
|---|---|
| `Job.OrderBy(j => j.Position)` | an array of rows |
| `Job.Count()` | a number — a scalar is a scalar, not a one-element array |
| `Job.Select(j => j.Title)` | an array of strings |
| `Job.First()` | one row |

A row shows the values the read actually brought back. A column your acting principal may not see is simply absent,
which is the honest rendering of what happened rather than a hole where a value would be.

### Reading as one of your users   {#as}
⭐ **`--as` takes no password, and that is the point.** Anonymous is the wrong default to be stuck with: on an app
with real security an anonymous read returns **nothing**, so the verb would be useless exactly where it matters —
and the only alternative would be reading with security off and returning **everything**. Neither answers the
question you actually have, which is *what does this user see?*

```console
$ osy query "Job.Count()"                       // as a signed-out visitor
$ osy query "Job.Count()" --as ada@example.com  // as Ada
$ osy query "Job.Count()" --as sam@example.com  // as Sam
```

Your `security { }` applies in every case. If the three answers differ, that IS your rules working — this is the
cheapest way to see them do it.

### Why that is safe, and where the line is   {#why-no-password}
⛔ **`osy query` refuses to WRITE.** An expression that creates, updates or deletes is rejected before it runs, and
the check is fail-closed: anything that cannot be proven read-only counts as writing.

That refusal is what makes the password-free `--as` sound. You already have developer authority over this app — you
can compile arbitrary code into it — so *reading* as one of its users grants you nothing you did not already have.
**Writing as them is impersonation**, which authority over an app does not confer over its people, so it stays
behind that user's own password:

```console
$ osy query "new Job { Title = \"sneaky\" }"
✗ `query` READS — this expression writes data, so it is refused here. Anything that creates, updates or
  deletes belongs in one of the app's own functions: run it with `osy run <Function>`.

$ osy run AddJob --arg title="re-plaster the hall" --as ada@example.com --password …
```

### Checking what you just wrote   {#after-a-run}
The pair is the point. `osy run` makes something happen; `osy query` says whether it did:

```console
$ osy run AddJob --arg title="re-plaster the hall"
$ osy query "Job.Count()"
1
```

## See also       {#see-also}
- [Running a function](https://osysharp.com/reference/local/running-a-function/) — the other half: making the app DO something
- [Importing data](https://osysharp.com/reference/local/importing-data/) — loading many rows from a file
- [Querying data](https://osysharp.com/reference/query/index/) — the query surface itself, and what becomes SQL
- [The security model](https://osysharp.com/reference/security/index/) — the rules `--as` is showing you
