---
term: 'Database Queries & Query Languages'
seoTitle: 'What is a Database Query? Query Languages Explained'
headline: 'What is a Database Query?'
slug: database-queries
category: database
shortDefinition: 'A database query is a structured request for data — filter, sort, project, paginate — written in a language the engine can plan and run.'
relatedTerms:
  - database-index
  - n-plus-one-query-problem
  - relational-queries-document-databases
  - graphql-vs-rest
contrastsWith:
  - auto-generated-database-apis
faq:
  - question: 'What is a database query in simple terms?'
    answer: 'A structured request asking the database to retrieve or change data — "give me published articles with over a thousand views, newest first, twenty at a time." It is written in a query language or built through an SDK, follows strict syntax so the engine can parse it, and returns exactly the slice of data it describes.'
  - question: 'Is SQL declarative or imperative?'
    answer: 'Declarative — you state what data you want, and the engine''s optimizer decides how to fetch it: which indexes to use, which join order, which algorithm. That division of labor is the core idea of modern querying, and it holds beyond SQL: document query APIs and GraphQL are declarative too. Imperative code loops over records; declarative queries describe results.'
  - question: 'How does a query actually execute?'
    answer: 'A four-stage pipeline: the engine parses the text into a syntax tree, validates it against the schema, plans and optimizes — choosing indexes, join orders, and algorithms by estimated cost — then executes the chosen plan and streams results. The plan is inspectable: EXPLAIN shows exactly what the optimizer decided, which is where all performance debugging starts.'
  - question: 'What is the anatomy of a query?'
    answer: 'Five verbs cover nearly everything: filter (which records — WHERE), sort (what order — ORDER BY), project (which fields — the SELECT list), paginate (how many, from where — LIMIT/OFFSET or a cursor), and aggregate (computed summaries — GROUP BY). Every query language and SDK expresses these same five; only the syntax changes.'
  - question: 'How do indexes make queries fast?'
    answer: 'They replace scanning with seeking: instead of reading every record to find matches (linear in table size), the engine walks a sorted structure straight to them (logarithmic). The difference is invisible at a thousand rows and decisive at ten million. EXPLAIN tells you which one your query is doing — a sequential scan on a large table is the classic red flag.'
  - question: 'What is SQL injection and how do parameterized queries prevent it?'
    answer: 'Injection is attacker input changing the structure of a query — the classic quote-and-comment tricks turning a login check into a tautology. Parameterized queries close the hole by separating code from data: the query text has placeholders, values are bound separately, and user input is only ever treated as a value — never parsed as query syntax. SDKs and query builders parameterize by construction.'
  - question: 'What is the difference between offset and cursor pagination?'
    answer: 'Offset pagination (skip N, take 20) is simple and can jump to any page, but the engine must count and discard skipped rows — page 500 costs more than page 1 — and concurrent writes can shift results between pages. Cursor pagination ("after this key, take 20") stays fast and stable at any depth, at the cost of no random page jumps. Feeds want cursors; small admin tables are fine with offsets.'
  - question: 'Do SDKs and query builders replace knowing queries?'
    answer: 'They replace writing the syntax, not understanding the semantics. A builder chain compiles to the same filter-sort-project-paginate anatomy and hits the same indexes — or misses them. The classic failure is the N+1 pattern: one query per item in a loop, invisible in the code, brutal in production. The anatomy and the cost model transfer across every surface.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Query language (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/Query_language'
  - name: 'PostgreSQL — Using EXPLAIN'
    url: 'https://www.postgresql.org/docs/current/using-explain.html'
  - name: 'GraphQL specification and docs'
    url: 'https://graphql.org/learn/'
  - name: 'SDK query documentation'
    url: 'https://docs.parseplatform.org/js/guide/#queries'
cta:
  title: 'Queries without the query language'
  text: 'On Back4app, the query builder in every SDK compiles to indexed, parameterized queries against your data — same five verbs, no injection surface, plus GraphQL when clients want to ask for exactly what they need.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-26'
translationKey: database-queries
---

**A database query is a structured request for data — filter, sort, project, paginate — written in a language the engine can plan and run.** The deep idea underneath every query language is *declarativeness*: you describe the result, and the engine chooses the path. Master the five verbs and the cost model once, and every syntax — SQL, document APIs, GraphQL, SDK builders — becomes an accent, not a new language.

## Key takeaways

| Question | Answer |
| --- | --- |
| What it is | A precise request: which records, what order, which fields, how many |
| The paradigm | Declarative — you say *what*; the optimizer decides *how* |
| The pipeline | Parse → validate → plan/optimize → execute |
| The cost model | Index seek vs. full scan — EXPLAIN tells you which you got |
| The security rule | Parameterize always; never concatenate input into queries |

## The Same Query in Four Query Languages

The same request — *published articles, over 1,000 views, newest first* — across the language families:

```sql
-- SQL: the original declarative
SELECT title, views FROM articles
WHERE  status = 'published' AND views > 1000
ORDER  BY published_at DESC
LIMIT  20;
```

```javascript
// Document query API
db.articles.find(
  { status: 'published', views: { $gt: 1000 } },  // filter
  { title: 1, views: 1 }                          // project
).sort({ publishedAt: -1 }).limit(20)

// GraphQL: clients declare the shape they want back
query {
  articles(where: { status: "published", views_gt: 1000 },
           orderBy: publishedAt_DESC, first: 20) {
    title
    views
  }
}
```

And the SDK builder — the surface most app code actually uses, compiling to the same anatomy:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Filter, sort, project, paginate — the full anatomy in one query
const query = new Parse.Query('Article');
query.equalTo('status', 'published');       // filter
query.greaterThan('views', 1000);           // filter (range)
query.descending('publishedAt');            // sort
query.select('title', 'views');             // project: only these fields
query.limit(20).skip(40);                   // paginate: page 3
const articles = await query.find();
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Filter, sort, project, paginate — the full anatomy in one query
final query = QueryBuilder<ParseObject>(ParseObject('Article'))
  ..whereEqualTo('status', 'published')     // filter
  ..whereGreaterThan('views', 1000)         // filter (range)
  ..orderByDescending('publishedAt')        // sort
  ..keysToReturn(['title', 'views'])        // project: only these fields
  ..setLimit(20)..setAmountToSkip(40);      // paginate: page 3
final response = await query.query();
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Filter, sort, project, paginate — the full anatomy in one query
let query = Article.query("status" == "published", "views" > 1000)
  .order([.descending("publishedAt")])      // sort
  .select("title", "views")                 // project: only these fields
  .limit(20).skip(40)                       // paginate: page 3
query.find { result in
  if case .success(let articles) = result { render(articles) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Filter, sort, project, paginate — the full anatomy in one query
val query = ParseQuery.getQuery<ParseObject>("Article")
query.whereEqualTo("status", "published")   // filter
query.whereGreaterThan("views", 1000)       // filter (range)
query.orderByDescending("publishedAt")      // sort
query.selectKeys(listOf("title", "views"))  // project: only these fields
query.limit = 20; query.skip = 40           // paginate: page 3
query.findInBackground { articles, e -> if (e == null) render(articles) }
```

## How a Database Executes a Query

```mermaid
flowchart LR
  accTitle: Query execution pipeline
  accDescr: The engine parses query text into a syntax tree, validates it against the schema, plans and optimizes by choosing indexes and join strategies based on cost, then executes the plan and returns results.
  Q["Query text"] --> P["Parse<br/>syntax tree"]
  P --> V["Validate<br/>against schema"]
  V --> O["Plan & optimize<br/>indexes · join order · cost"]
  O --> E["Execute<br/>stream results"]
```

The optimizer is the reason declarative querying works: it weighs the available [indexes](/glossary/database-index/), estimates row counts, and picks the cheapest plan — and [EXPLAIN](https://www.postgresql.org/docs/current/using-explain.html) shows its decision. The one-line performance method: run EXPLAIN, look for the full scan, add the index the filter is begging for, look again.

## Declarative vs. imperative

| Dimension | Declarative query | Imperative code |
| --- | --- | --- |
| You write | The result you want | The steps to compute it |
| Who optimizes | The engine, per execution | You, once, at author time |
| Adapts to data size | Yes — plans change with statistics | No — the loop is the loop |
| Where it lives | SQL, document APIs, GraphQL, builders | Application loops over records |
| Failure smell | Wrong plan (fix with indexes/hints) | N+1 loops, memory blowups |

The anatomy that maps across all of them: **filter** (`WHERE`), **sort** (`ORDER BY`), **project** (the SELECT list — ask only for what you need), **paginate** (`LIMIT` plus offset or cursor), **aggregate** (`GROUP BY` and friends). Five verbs, every surface.

## The two rules that prevent most query incidents

**Parameterize, always.** Injection is attacker input becoming query *structure* — solved completely by placeholders that bind input as values:

```javascript
// Vulnerable: input concatenated into the query's structure
db.query(`SELECT * FROM users WHERE name = '${input}'`);   // never this

// Safe: parameterized — input is data, not syntax
db.query('SELECT * FROM users WHERE name = $1', [input]);
```

SDK builders and GraphQL variables do this by construction — one of the quiet security wins of higher-level query surfaces.

**Paginate for the shape of access.** Offset pagination (`LIMIT 20 OFFSET 400`) can jump to any page but pays linearly for depth and wobbles under concurrent writes; cursor pagination (`WHERE published_at < $last`) is stable and constant-cost at any depth but only moves forward. Feeds and infinite scroll want cursors; page-numbered admin tables are honest offset territory.

## Common use cases

- **Application reads.** List views, detail screens, search — the filter-sort-project-paginate quartet in its natural habitat.
- **Aggregation and reporting.** Counts, sums, grouped summaries — pushed to the engine, where the data is, instead of computed in app code.
- **API surfaces.** [Auto-generated APIs](/glossary/auto-generated-database-apis/) translate URL parameters or GraphQL selections into these same queries — the anatomy leaks through every abstraction.
- **Real-time filters.** Live subscriptions are standing queries — the same predicates, continuously evaluated.
- **Debugging production.** EXPLAIN plus the slow-query log is the diagnostic loop for the "it got slow" class of incident.

## Raw language, builder, or SDK? A decision matrix

| Reach for… | When… | Watch for… |
| --- | --- | --- |
| Raw query language | Complex aggregation, reports, migrations | Injection if you concatenate; portability |
| Query builder / SDK | Application CRUD and lists — most code | N+1 loops; builders hide the plan |
| GraphQL | Clients need to pick fields and nest relations | Unbounded query cost without limits |
| Stored/server-side logic | Multi-step operations near the data | Logic hidden from the codebase |

The honest rule from the [abstraction-layer discussion](/glossary/database-abstraction-layer/) applies verbatim: builders for the routine 90%, raw queries where control earns its keep — and the anatomy knowledge transfers either way.

## Limitations and trade-offs

- **Declarative isn't free.** The optimizer is only as good as its statistics and indexes; a right query on wrong indexes is still slow.
- **Queries hide their cost.** One line of builder chain can be a scan over millions of rows; EXPLAIN is the only honest mirror.
- **The N+1 trap lives above the query.** Perfect individual queries, issued in a loop, are collectively pathological — batching and includes exist for this.
- **Deep pagination degrades.** Offset depth is linear cost; design feeds around cursors from day one.
- **Language sprawl is real.** SQL, document APIs, GraphQL, search DSLs — teams pay a tax per surface; the shared anatomy is the antidote.

## Queries on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Its query story is the SDK-builder path done thoroughly: the [query builder](https://docs.parseplatform.org/js/guide/#queries) in every SDK — the code tabs above — compiles filter, sort, project, and paginate into parameterized queries with no injection surface, `include()` handles relations without N+1 loops, the same predicates power [live queries](https://www.back4app.com) for real-time updates, and GraphQL serves clients that want to declare their own shapes. The five verbs, every surface, one backend.
