3.3 KiB
Discovery and overrides
restloom reads the database's catalog once, on the first request, and builds the API from it. rest.reload() reads it again on the next request, after a migration for example.
What becomes a resource
expose names what is served. Nothing else is.
expose: { schemas: ['app'] } // every table and view of app
expose: { tables: ['app.todos', 'app.users'] } // only these
A resource is named after its table. When two exposed schemas hold a table of the same name, both are named schema_table. resources.<name>.expose: false removes one, for example a junction table you only want used as a relation:
resources: { todo_tags: { expose: false } }
A resource's key is its primary key. A view has none, so it uses a column named id when it has one, or the columns you give it: resources: { todo_rows: { key: ['todo_id'] } }. A resource without a key can still be listed, but has no /{id} path and no stable page order.
Columns
Every column is visible except:
- Sensitive names: columns whose name contains one of these words:
password,passwd,pwd,hash,hashed,salt,secret,token,api_key,private_key,otp,totp,mfa,recovery. A word is a whole part of the name between underscores:password_hashandreset_tokenare hidden,tokenizeris not. - Columns you hide:
resources: { users: { hide: ['phone'] } }.
show makes a sensitive-looking column visible on purpose: resources: { files: { show: ['sha256_hash'] } }. sensitive replaces the whole list with your own pattern.
A hidden column cannot be read, filtered, sorted or written, and naming it answers exactly as naming a column that does not exist. It can still join a relation.
Relations
Relations come from foreign keys between exposed resources:
| In the catalog | Gives |
|---|---|
todos.owner_id references users.id |
todos.owner, to-one; users.todos, to-many |
todo_tags has a primary key made of a key to todos and a key to tags |
todos.tags and tags.todos, many-to-many |
Names:
- To-one: the foreign key column without
_id(owner_idgivesowner). Otherwise the target table's name. - To-many: the source table's name.
- Collisions: when a name is taken by a column or another relation, the next candidate is used. For to-one, that is the target table's name, then
<column>_<table>. For to-many, it is<table>_by_<column>, and for many-to-many<table>_via_<junction>.
A relation to a table that is not exposed does not exist. A foreign key to a hidden table exposes nothing.
relations overrides, adds or removes them:
resources: {
users: {
relations: {
users: false, // remove the discovered one
reports: { many: 'users', via: 'manager_id' }, // to-many: users whose manager_id is my key
team: { one: 'teams', via: 'team_id' }, // to-one, e.g. for a view without foreign keys
badges: { many: 'badges', through: 'user_badges' },
},
},
}
An override with the name of a column is refused when the catalog is read, and so is any option naming a resource, column or table that does not exist. Call await rest.resources() at start so a typo stops your server from booting instead of failing its first request.