---
term: 'Campos Pointer vs. Relation na Modelagem em BaaS'
seoTitle: 'Pointer vs. Relation: Guia de Modelagem de Dados em BaaS'
headline: 'Qual a diferença entre campos pointer e relation?'
slug: pointers-vs-relations
category: database
shortDefinition: 'Um campo pointer é uma referência tipada a um único objeto; um campo relation é um join gerenciado que liga muitos objetos a muitos.'
relatedTerms:
  - data-modeling
  - relational-queries-document-databases
  - database-schema
  - auto-generated-database-apis
contrastsWith:
  - relational-queries-document-databases
aboutTerms:
  - 'Campos Pointer'
  - 'Campos Relation'
faq:
  - question: 'Qual a diferença entre pointer e relation em um modelo de dados de BaaS?'
    answer: 'O pointer guarda uma única referência dentro do próprio objeto — uma chave estrangeira com tipo — então cada objeto aponta para exatamente um alvo. A relation guarda um conjunto ilimitado de referências em uma estrutura de junção que a plataforma mantém escondida. Pointers modelam um-para-um e um-para-muitos; relations modelam muitos-para-muitos, onde as listas de participação crescem sem teto.'
  - question: 'Como modelar um relacionamento um-para-muitos com pointers?'
    answer: 'Coloque o pointer no lado "muitos": cada Comment carrega um pointer para o seu Post, exatamente como uma chave estrangeira faria. Para listar os comentários de um post, consulte a classe Comment onde o pointer é igual àquele post. Cada objeto continua pequeno, as escritas continuam baratas e o padrão funciona em qualquer escala — a quantidade de filhos nunca incha o pai.'
  - question: 'Quando usar um array de pointers em vez de uma relation?'
    answer: 'Quando a lista é pequena, limitada e quase sempre lida junto com o pai — os dez ingredientes de uma receita, os itens de um pedido. Arrays viajam dentro do objeto, então um único include() traz tudo; mas cada elemento engorda o objeto, e passando de algumas centenas de entradas as leituras e os saves ficam lentos. Listas ilimitadas ou compartilhadas pertencem a relations.'
  - question: 'Dá para consultar através de um pointer sem buscar os dois objetos separadamente?'
    answer: 'Sim — é exatamente o que o include() faz. Consulte Posts com include(''author'') e a plataforma resolve cada pointer no servidor, devolvendo os objetos de autor completos em uma resposta só; a notação de ponto vai mais fundo, como em include(''author.company''). O filtro funciona na direção oposta também: matchesQuery() seleciona os pais por condições no objeto apontado.'
  - question: 'Como os campos relation funcionam por baixo dos panos?'
    answer: 'A plataforma mantém uma tabela de junção escondida para cada campo relation, guardando pares de IDs de objeto — a mesma estrutura que um schema relacional chamaria de tabela de junção, sem que você precise projetá-la ou nomeá-la. As consultas de participação batem direto nessa estrutura, então nenhum dos dois lados do relacionamento cresce de tamanho, não importa quantos vínculos se acumulem.'
  - question: 'Campos pointer são indexados automaticamente?'
    answer: 'Não parta desse pressuposto — trate campos pointer como qualquer outro filtro de query e indexe aqueles que suas consultas atravessam. Um lookup um-para-muitos varre a classe filha procurando um pointer correspondente, e sem índice isso é um full scan da coleção. A regra de sempre da indexação vale igual aqui: indexe o que você consulta, principalmente os caminhos de join.'
  - question: 'O que é mais rápido, pointer ou relation?'
    answer: 'Pointers, em geral — eles resolvem dentro da mesma query via include(), sem estrutura de junção para consultar. Relations custam uma query extra ou um join interno contra a tabela escondida; esse é o preço da cardinalidade ilimitada. A regra prática: use a ferramenta mais barata que a cardinalidade permitir — pointer primeiro, array segundo, relation por último.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Parse SDK guide — relational data'
    url: 'https://docs.parseplatform.org/js/guide/'
  - name: 'One-to-many data model (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/One-to-many_(data_model)'
  - name: 'MongoDB — model relationships between documents'
    url: 'https://www.mongodb.com/docs/manual/applications/data-models-relationships/'
  - name: 'Cardinality in data modeling (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/Cardinality_(data_modeling)'
cta:
  title: 'Modele relacionamentos sem projetar tabelas de junção'
  text: 'O Back4app entrega pointer e relation como tipos de coluna de primeira classe: monte um-para-muitos e muitos-para-muitos no dashboard, consulte através deles com include() a partir de qualquer SDK e deixe a plataforma manter as estruturas de junção.'
  linkText: 'Comece grátis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-25'
translationKey: pointer-vs-relation-fields
---

**Um campo pointer é uma referência tipada a um único objeto; um campo relation é um join gerenciado que liga muitos objetos a muitos.** Os dois respondem à pergunta que todo schema enfrenta — como os registros referenciam uns aos outros? — mas em cardinalidades diferentes e a custos diferentes. Escolher entre eles é a decisão central da [modelagem de dados](/glossary/pt/modelagem-de-dados/) em um BaaS, e a boa notícia é que a decisão se comprime em uma pergunta: quantos, de cada lado?

## Principais pontos

| Pergunta | Resposta |
| --- | --- |
| Pointer | Uma referência tipada guardada *dentro* do objeto — chave estrangeira com classe |
| Relation | Um conjunto ilimitado de referências em uma tabela de junção gerenciada pela plataforma |
| Um-para-muitos | Pointer no lado "muitos", sempre |
| Muitos-para-muitos | Relation — ou um array de pointers quando a lista é pequena e limitada |
| As ferramentas de travessia | `include()` resolve pointers; uma query de relation busca o conjunto da junção |

## As duas formas, em código

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Pointer: one query, one hop — include() resolves the reference server-side
const posts = new Parse.Query('Post');
posts.equalTo('status', 'published');
posts.include('author');                  // pointer → full Author object
const page = await posts.find();
const name = page[0].get('author').get('displayName');

// Relation: the unbounded join set gets its own query
const tags = await page[0].relation('tags').query().find();
// tags is a plain array of Tag objects — the join table stays invisible
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Pointer: one query, one hop — includeObject resolves the reference server-side
final posts = QueryBuilder<ParseObject>(ParseObject('Post'))
  ..whereEqualTo('status', 'published')
  ..includeObject(['author']);            // pointer → full Author object
final response = await posts.query();
final post = response.results!.first as ParseObject;
final author = post.get<ParseObject>('author');

// Relation: the unbounded join set gets its own query
final relation = post.getRelation('tags');
final tags = await relation.getQuery().query();
// tags.results is a plain list of Tag objects — the join table stays invisible
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Pointer: one query, one hop — include() resolves the reference server-side
let posts = Post.query("status" == "published")
  .include("author")                      // pointer → full Author object
posts.find { result in
  if case .success(let page) = result {
    print(page.first?.author?.displayName ?? "")
  }
}

// Relation: the unbounded join set gets its own query
let tags = Tag.query(related(key: "tags", object: try post.toPointer()))
tags.find { result in
  if case .success(let tagList) = result { render(tagList) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Pointer: one query, one hop — include() resolves the reference server-side
val posts = ParseQuery.getQuery<ParseObject>("Post")
posts.whereEqualTo("status", "published")
posts.include("author")                   // pointer → full Author object
posts.findInBackground { page, e ->
    val author = page?.firstOrNull()?.getParseObject("author")
    println(author?.getString("displayName"))
}

// Relation: the unbounded join set gets its own query
val relation = post.getRelation<ParseObject>("tags")
relation.query.findInBackground { tags, e ->
    if (e == null) render(tags)           // the join table stays invisible
}
```

O equivalente em banco relacional deixa o mapeamento explícito — um pointer é uma chave estrangeira tipada; uma relation é uma tabela de junção que você nunca precisa criar:

```sql
-- Pointer: uma coluna na linha filha (um-para-muitos)
CREATE TABLE comment (
  id         serial PRIMARY KEY,
  post_id    integer REFERENCES post(id),   -- ← o "pointer"
  body       text
);

-- Relation: uma tabela de junção (muitos-para-muitos) — um BaaS cria isso para você
CREATE TABLE post_tags (
  post_id    integer REFERENCES post(id),
  tag_id     integer REFERENCES tag(id),
  PRIMARY KEY (post_id, tag_id)
);
```

## Como cada um resolve na hora da query

```mermaid
flowchart TB
  accTitle: Resolução de pointer vs. travessia de relation
  accDescr: Uma query com pointer resolve o objeto referenciado inline através do include, devolvendo uma única resposta. Uma query de relation primeiro consulta uma tabela de junção escondida com pares de IDs e depois busca os objetos correspondentes do outro lado.
  Q1["Query em Post<br/>include('author')"] --> P["Pointer resolvido inline"]
  P --> R1["Uma resposta:<br/>posts + autores completos"]
  Q2["post.relation('tags')<br/>.query()"] --> J["Tabela de junção escondida<br/>pares (postId, tagId)"]
  J --> R2["Segunda busca:<br/>objetos Tag correspondentes"]
```

O caminho do pointer é o barato: [`include()`](https://docs.parseplatform.org/js/guide/) manda o servidor trocar cada referência pelo objeto completo antes de responder — um round trip só, profundidade arbitrária via notação de ponto (`include('author.company')`), e a cura padrão para o padrão N+1 de buscar filhos dentro de um loop. O filtro funciona no mesmo salto: `equalTo('author', pointer)` acha os comentários de um post, e `matchesQuery()` filtra uma classe por condições em outra — o kit completo está em [consultas relacionais em bancos de documentos](/glossary/pt/joins-em-bancos-de-documentos/).

O caminho da relation compra outra coisa. Como a participação vive na estrutura de junção, e não em nenhum dos dois objetos, um usuário pode pertencer a dez mil grupos e um grupo pode conter um milhão de usuários sem que nenhum documento cresça um byte sequer. O custo é um salto extra: `include()` não atravessa relations — o conjunto da junção ganha a própria query.

Entre os dois fica o **array de pointers**: um campo de lista guardando referências tipadas. É a economia do pointer aplicada a um conjunto pequeno — um `include()` traz todos os elementos — mas o array vive dentro do objeto, então cada elemento deixa o objeto mais pesado para ler, salvar e sincronizar. Passando de algumas centenas de entradas, o objeto contêiner vira o gargalo, e esse é o sinal de que você modelou uma relation como array.

## Pointer vs. array de pointers vs. relation

### Qual ferramenta de relacionamento você deve usar?

| Dimensão | Pointer | Array de pointers | Relation |
| --- | --- | --- | --- |
| Cardinalidade | Um alvo | Poucos, limitados | Ilimitada, muitos-para-muitos |
| Onde é guardado | No objeto | No objeto | Tabela de junção escondida |
| Buscar junto com o pai | `include()` | `include()` | Query de relation separada |
| Crescimento do objeto | Nenhum | Por elemento | Nenhum, nunca |
| Ordenação | não se aplica | Preservada | Sem garantia |
| Exemplo canônico | Comment → Post | Pedido → itens de linha | Users ↔ Groups |

Dois detalhes merecem atenção. Arrays preservam a ordem dos elementos — relations não —, então uma lista ordenada (faixas de uma playlist, etapas de um workflow) é uma questão de array independentemente da pressão de tamanho. E o um-para-muitos tem duas codificações: pointer-no-filho escala indefinidamente, array-no-pai é mais conveniente de ler; quem decide é o teto de [cardinalidade](https://en.wikipedia.org/wiki/Cardinality_(data_modeling)), não o gosto.

## Casos de uso comuns

- **Posse e autoria.** `createdBy`, `author`, `owner` — pointers um-para-um e um-para-muitos; a referência que toda classe acaba carregando.
- **Threads de comentários e feeds de atividade.** Pointer no filho (`comment.post`), consultado pelo pai — o cavalo de batalha do um-para-muitos ilimitado.
- **Tags e categorização.** Posts ↔ tags, produtos ↔ coleções: muitos-para-muitos, os dois lados ilimitados — relations.
- **Seguidores e participação em grupos.** O grafo social clássico — relations, porque a lista de seguidores de uma conta popular não pode morar dentro do objeto da conta.
- **Itens de linha e conjuntos pequenos e ordenados.** Limitados, lidos junto com o pai, com ordem importando — é aqui que arrays de pointers justificam a conveniência.

## Você deveria usar um pointer ou uma relation? Matriz de decisão

| Vá de pointer quando… | Vá de relation quando… |
| --- | --- |
| Cada objeto referencia exatamente um alvo | Os dois lados podem crescer sem limite |
| É um-para-muitos (pointer no filho) | É genuinamente muitos-para-muitos |
| Você quer que o `include()` traga junto com o pai | O conjunto é consultado sozinho, não junto com o pai |
| A referência participa de filtros e ordenações | Checagens de participação dominam (X está no grupo Y?) |
| Uma lista limitada e ordenada cabe num array | Um campo array está visivelmente inchando o objeto |

A heurística comprimida: **pointer primeiro, array segundo, relation por último** — só escale quando a cardinalidade obrigar. A maioria dos schemas termina esmagadoramente baseada em pointers, com um punhado de relations de verdade carregando as arestas sociais ou de taxonomia. Desenhe o [schema](/glossary/pt/schema-de-banco-de-dados/) em torno das queries que você vai realmente rodar e depois indexe os campos pointer que essas queries atravessam.

## Limitações e trade-offs

- **Relations custam um salto extra.** Não existe `include()` através de uma relation — buscar os membros é uma segunda query, e contá-los no servidor é a única forma sã em escala.
- **Arrays incham em silêncio.** O modo de falha é gradual: o objeto que guardava 20 pointers guarda 2.000 um ano depois, e toda leitura paga a conta. Defina um teto quando escolher o array.
- **Pointers precisam de índice como qualquer filtro.** Consultar filhos pelo pointer do pai sem índice é um scan de coleção vestido de API conveniente.
- **Não existe delete em cascata.** Apagar um post não apaga os comentários dele nem limpa suas relations — cuidar dos órfãos é tarefa sua, tipicamente em um gatilho de delete no servidor.
- **A integridade referencial é apenas indicativa.** Um pointer pode referenciar um objeto já apagado; a plataforma não vai impedir. Trate referências penduradas como um estado que seu código pode encontrar.

## Pointers e relations no Back4app

O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. Pointer e relation são tipos de coluna de primeira classe no schema dele: crie qualquer um dos dois no dashboard e as [APIs geradas automaticamente](/glossary/pt/apis-geradas-automaticamente/) já suportam na hora `include()`, queries de relation e filtros entre classes a partir de qualquer SDK — as abas de código acima rodam sem alteração. As tabelas de junção por trás das relations são criadas, nomeadas e mantidas pela plataforma, e os gatilhos de Cloud Code são a casa natural para a lógica de delete em cascata e limpeza de órfãos que o modelo em si deixa por sua conta.
