# Bases de données

> La connexion, les tables, les lignes, et du SQL — sans ouvrir la base sur Internet.

Une base de données est un service de famille `APP` monté depuis le catalogue. Elle est
**fermée sur Internet par défaut** : ses voisins de projet la joignent par son alias, et
ces routes te la font lire depuis l'extérieur sans rien ouvrir — l'API passe par un
tunnel vers le conteneur.

## La connexion

```http
GET /api/resources/{id}/database/connection
```

```json
{
  "engine": "POSTGRES",
  "host": "crm-db.arkya.internal",
  "port": 30015,
  "user": "arkya",
  "password": "SllaBNYQ…",
  "database": "arkya",
  "url": "postgres://arkya:SllaBNYQ…@crm-db.arkya.internal:30015/arkya"
}
```

`host` est le **nom interne** du service, en `.arkya.internal` : il ne se résout que depuis
le réseau du projet, et nulle part ailleurs. C'est
l'adresse à donner à un service voisin, pas à ton poste. Depuis l'extérieur, ouvre le
réseau ([`PUT /network`](/api/reglages#réseau)) ou passe par les routes ci-dessous.

<Note>
Plutôt que de copier ces valeurs dans une variable, écris `${crm-db.URL}` dans
l'environnement du service qui consomme : la référence est résolue au démarrage, et elle
suit la base si son port change. Voir [les références entre
services](/api/variables#les-références-entre-services).
</Note>

## L'inventaire

```http
GET /api/resources/{id}/database
```

```json
{
  "engine": "POSTGRES",
  "supported": true,
  "database": "arkya",
  "version": "PostgreSQL 16.14",
  "tables": [
    { "schema": "public", "name": "joueurs", "rows": 128, "bytes": 32768 }
  ]
}
```

`rows` est l'estimation du moteur, pas un compte exact : elle est là pour classer, pas
pour facturer. `supported` à `false` marque un moteur qu'on ne sait pas encore ouvrir
ici — MySQL et Redis répondent alors à la connexion, mais pas aux deux routes suivantes.

## Les lignes d'une table

```http
GET /api/resources/{id}/database/rows?schema=public&table=joueurs&page=0
```

```json
{
  "columns": [
    { "name": "id", "type": "23" },
    { "name": "pseudo", "type": "25" }
  ],
  "rows": [["1", "ark"], ["2", "dev"]],
  "rowCount": 2,
  "truncated": false,
  "elapsedMs": 42,
  "command": "SELECT"
}
```

Cinquante lignes par page. Les valeurs arrivent **toutes en chaînes**, `null` restant
`null` : c'est ce qui permet de rendre n'importe quelle colonne sans connaître son type.
La table est vérifiée contre le catalogue du moteur avant d'être citée dans la requête.

## Une requête

```http
POST /api/resources/{id}/database/query
```

```json
{ "sql": "select pseudo, score from joueurs order by score desc limit 10" }
```

La réponse a la même forme que les lignes. `rowCount` compte ce que l'ordre a touché —
pour un `INSERT` ou un `UPDATE`, `columns` est vide et `rowCount` dit combien de lignes
ont bougé.

- Toute requête est coupée à 15 secondes.
- Au-delà de 500 lignes rendues, `truncated` passe à `true` : pagine avec `limit` et
  `offset`.
- La requête part avec les droits du compte de la base, les tiens : un `drop table`
  passera.

<Warning>
Cette route exécute ce que tu lui donnes. C'est une console, pas un bac à sable — traite
le `sql` que ton programme construit avec la même prudence que sur n'importe quelle base.
</Warning>
