Skip to content

Commit

Permalink
editorial down to muliple-schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
jemgillam committed Jan 28, 2025
1 parent 0f26645 commit 92ee5af
Show file tree
Hide file tree
Showing 36 changed files with 398 additions and 385 deletions.
4 changes: 2 additions & 2 deletions postgraphile/website/postgraphile/aggregates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Aggregates
---

PostGraphile's engine has support for powerful aggregates. The
PostGraphiles engine has support for powerful aggregates. The
[@graphile/pg-aggregates](https://github.com/graphile/pg-aggregates) module
adds various aggregates to the schema, and gives you the ability to add more
via plugins. Aggregates are located under connection fields.
Expand All @@ -19,7 +19,7 @@ these on the client).
### Aggregates only work on Relay connection

Thanks to their expansibility, relay [connections](./connections) were the
perfect place to add aggregates support. If you're using a behavior
perfect place to add aggregates support. If youre using a behavior
configuration that prefers lists over connections (e.g. `-connection +list`)
then you can override it on a per-collection basis with the [`@behavior
+connection` smart tag](./smart-tags/#behavior).
4 changes: 2 additions & 2 deletions postgraphile/website/postgraphile/background-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Other important considerations when choosing a task queue would be:
- Complexity
- Throughput requirements
- DevOps complexity
- Tracability
- Traceability
- Privacy

### Graphile Worker
Expand Down Expand Up @@ -94,5 +94,5 @@ on how to set up and use it.
Despite all the benefits, Graphile Worker stills lacks some features provided by
other more mature tasks queues

- Currently Graphile Worker doesn't expose a GUI to monitor or manage tasks.
- Currently Graphile Worker doesnt expose a GUI to monitor or manage tasks.
- ??
94 changes: 47 additions & 47 deletions postgraphile/website/postgraphile/behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

For all but the simplest APIs you will want to control which parts of your data
sources are exposed and how they are exposed. New to PostGraphile version 5 is
the "behavior" system that gives you granular control over these topics.
the behavior system that gives you granular control over these topics.

## Terminology

A "behavior string" is a text-based string with a relatively simple syntax; here
A behavior string is a text-based string with a relatively simple syntax; here
are some examples:

- `insert`
- `+list -connection -list:filter`
- `-insert -update -delete query:*:filter +connection -list`

A behavior string is made of a list of "behavior fragments" separated by spaces.
A behavior string is made of a list of behavior fragments separated by spaces.
Each behavior fragment optionally starts with a `+` or `-` symbol (if neither is
specified then `+` is inferred) followed by a "scope string." A scope string is
one or more "scope phrases" joined by colons (`:`). A scope phrase is either a
specified then `+` is inferred) followed by a scope string. A scope string is
one or more scope phrases joined by colons (`:`). A scope phrase is either a
simple alphanumeric word (in camelCase), or an asterisk (`*`).

## Determining entity behavior
Expand All @@ -25,7 +25,7 @@ Many entities that PostGraphile processes when generating a schema (for example:
tables, columns, functions, types, etc.) have associated behaviors which
influence whether and how that entity is exposed. You may influence their
resulting behaviors by adding your own behavior strings to the entity, either
directly or via smart tags/smart comments. For example, if you don't want users
directly or via smart tags/smart comments. For example, if you dont want users
to be able to modify entries in the `forums` table, you might add a database
comment such as `comment on table forums is '@behavior -insert -update
-delete';` (this is just one of many ways of attaching behaviors).
Expand All @@ -44,16 +44,16 @@ strings from various sources; typically this follows the following pattern:
The highest precedence behaviors are at the end the behavior string, and the
lowest priority behaviors are at the start.

When determining if an entity possesses a given "filter" behavior, the system
will scan backwards through the entity's behavior string for the first fragment
When determining if an entity possesses a given filter behavior, the system
will scan backwards through the entitys behavior string for the first fragment
that matches the filter; if the matching fragment has a `-` modifier then the
entity does not possess that behavior (even if a positive fragment existed
earlier in the behavior string) otherwise it does.

## Global default behavior

If you want to make wide-sweeping changes to behaviors, you can add "default
behaviors" via the `preset.schema.defaultBehavior` setting. For example if you
If you want to make wide-sweeping changes to behaviors, you can add default
behaviors via the `preset.schema.defaultBehavior` setting. For example if you
want your schema to use lists by default, eschewing the more verbose (but
typically superior) connections pattern, you might have a configuration
something like this:
Expand All @@ -72,23 +72,23 @@ const preset = {
export default preset;
```

These global defaults can still be overridden by each entity, so they're a
good way of making wide ranging "default" behaviors without locking yourself in
These global defaults can still be overridden by each entity, so theyre a
good way of making wide ranging default behaviors without locking yourself in
too hard.

:::info
:::info Global behavior in presets

If you're authoring a preset that is not the final configuration for a schema
If youre authoring a preset that is not the final configuration for a schema
then this default behavior setting is likely to be overridden (replaced) by the
user's configuration. Instead, preset authors should add a plugin that has a
users configuration. Instead, preset authors should add a plugin that has a
`schema.globalBehavior` entry. If this entry is a string, the behavior will be
prepended. If it's a callback, then it should return an array of behavior
prepended. If its a callback, then it should return an array of behavior
strings to be joined, one of which should be the current (passed in) behavior.
Typically you want a tuple where the first entry is your new behaviors and the
second entry is the current (passed-in) behavior (this way the user's
second entry is the current (passed-in) behavior (this way the users
`defaultBehavior` will have higher precedence to your `globalBehavior`).

A similar "lists by default" plugin might look like this:
A similar lists by default plugin might look like this:

```js
const FavourListsPlugin = {
Expand Down Expand Up @@ -130,11 +130,11 @@ is narrower than `update`; `constraint:resource:update` is narrower still).
:::

The following are behaviors that the core
PostGraphile/graphile-build/graphile-build-pg plugins utilise:
PostGraphile/graphile-build/graphile-build-pg plugins utilize:

- `select` - can select this resource/column/etc. Note this does not necessarily
mean you can do `select * from users` but it might mean that it's possible to
see details about a `users` when it's returned by a function or similar. (In
mean you can do `select * from users` but it might mean that its possible to
see details about a `users` when its returned by a function or similar. (In
this case the `codec` has `select` but the `resource` has `-select`.)
- `resource:select` - can select rows from this resource
- `insert:resource:select` - can select the row that was inserted (on the mutation payload)
Expand All @@ -144,47 +144,47 @@ PostGraphile/graphile-build/graphile-build-pg plugins utilise:
- `resource:insert` - can insert into this resource
- `resource:update` - can update a record in this resource
- `resource:delete` - can delete a record in this resource
- `resource:list` - "list" field for a resource at any level
- `resource:connection` - "connection" field for a resource at any level
- `resource:list:filter` - can we filter this resource's results (when represented as a list)?
- `resource:list:order` - can we order this resource's results (when represented as a list)?
- `resource:connection:filter` - can we filter this resource's results (when represented as a connection)?
- `resource:connection:order`- can we order this resource's results (when represented as a connection)?
- `resource:connection:backwards` - can we paginate backwards through this resource's results (when represented as a connection)?
- `resource:list` - list field for a resource at any level
- `resource:connection` - connection field for a resource at any level
- `resource:list:filter` - can we filter this resources results (when represented as a list)?
- `resource:list:order` - can we order this resources results (when represented as a list)?
- `resource:connection:filter` - can we filter this resources results (when represented as a connection)?
- `resource:connection:order`- can we order this resources results (when represented as a connection)?
- `resource:connection:backwards` - can we paginate backwards through this resources results (when represented as a connection)?
- `resource:aggregates` - does this resource support aggregates across its attributes (i.e. does the `aggregates` field get added to connections)
- `resource:groupedAggregates` - does this resource support grouped aggregates across its attributes (i.e. does the `groupedAggregates` field get added to connections)
- `resource:groupedAggregates:having` - can groupedAggregates on this resource have a 'having' clause?
- `sum:resource:groupedAggregates:having` - can groupedAggregates on this resource have a 'having' clause that uses the 'sum' aggregate?
- `sum:resource:aggregates` - does this resource support 'sum' aggregates across its attributes (replace 'sum' with the aggregate id you care about) (i.e. does the `sum` field get added within the `aggregates` field on connections?)
- `resource:groupedAggregates:having` - can groupedAggregates on this resource have a having clause?
- `sum:resource:groupedAggregates:having` - can groupedAggregates on this resource have a having clause that uses the sum aggregate?
- `sum:resource:aggregates` - does this resource support sum aggregates across its attributes (replace sum with the aggregate id you care about) (i.e. does the `sum` field get added within the `aggregates` field on connections?)
- `constraint:resource:update` - can update a record by this constraint
- `constraint:resource:delete` - can delete a record by this constraint
- `nodeId:resource:update` - can update a record by its NodeID
- `nodeId:resource:delete` - can delete a record by its NodeID
- `attribute:select` - can this attribute be selected?
- `attribute:insert` - can this attribute be inserted into?
- `attribute:update` - can this attribute be updated?
- `attribute:base` - should we add this attribute to the "base" input type?
- `attribute:base` - should we add this attribute to the base input type?
- `attribute:aggregate` - can we aggregate on this attribute?
- `sum:attribute:aggregate` - can we perform the 'sum' aggregate on this attribute? (replace 'sum' with the aggregate id you care about) (i.e. does this attribute get added to the `sum` field on the `aggregates` field on connections?)
- `sum:resource:aggregate` - does this computed-column-like resource support the 'sum' aggregate when acting as an attribute (replace 'sum' with the aggregate id you care about)
- `sum:attribute:aggregate` - can we perform the sum aggregate on this attribute? (replace sum with the aggregate id you care about) (i.e. does this attribute get added to the `sum` field on the `aggregates` field on connections?)
- `sum:resource:aggregate` - does this computed-column-like resource support the sum aggregate when acting as an attribute (replace sum with the aggregate id you care about)
- `attribute:groupBy` - can we group by this attribute when performing grouped aggregates?
- `attribute:havingBy` - can this attribute be used in the 'having' clause of a grouped aggregate?
- `sum:attribute:havingBy` - can the sum of this attribute be used in the 'having' clause of a grouped aggregate?
- `resource:havingBy` - can this computed-column-like resource be used in the 'having' clause of a grouped aggregate?
- `sum:resource:havingBy` - can the sum of this computed-column-like resource be be used in the 'having' clause of a grouped aggregate?
- `attribute:havingBy` - can this attribute be used in the having clause of a grouped aggregate?
- `sum:attribute:havingBy` - can the sum of this attribute be used in the having clause of a grouped aggregate?
- `resource:havingBy` - can this computed-column-like resource be used in the having clause of a grouped aggregate?
- `sum:resource:havingBy` - can the sum of this computed-column-like resource be be used in the having clause of a grouped aggregate?
- `nodeId:insert` - can we insert to the columns represented by this nodeId which represents a table related via foreign key constraint?
- `nodeId:update` - can we update the columns represented by this nodeId which represents a table related via foreign key constraint?
- `nodeId:base` - should we add a nodeId input representing this foreign key constraint to the "base" input type?
- `nodeId:base` - should we add a nodeId input representing this foreign key constraint to the base input type?
- `type:node` - should the GraphQLObjectType (`type`) this codec represents
implement the GraphQL Global Object Identification specification
- `interface:node` - should the GraphQLInterfaceType (`interface`) this codec
represents implement the GraphQL Global Object Identification specification
- `list` - list (simple collection)
- `connection` - connection (GraphQL Cursor Pagination Spec)
- `query:resource:list` - "list" field for a resource at the root Query level
- `query:resource:connection` - "connection" field for a resource at the root Query level
- `query:interface:list` - "list" field for a interface at the root Query level
- `query:interface:connection` - "connection" field for a interface at the root Query level
- `query:resource:list` - list field for a resource at the root Query level
- `query:resource:connection` - connection field for a resource at the root Query level
- `query:interface:list` - list field for a interface at the root Query level
- `query:interface:connection` - connection field for a interface at the root Query level
- `queryField` - for procedures: should it become a field on the `Query` type?
- `typeField` - for procedures: should it become a field on a non-operation
type?
Expand All @@ -202,15 +202,15 @@ PostGraphile/graphile-build/graphile-build-pg plugins utilise:
- `proc:orderBy` - can we order by the result of this proc (function resource)?
- `attribute:orderBy` - can we order by this attribute (column, property)?
- `attribute:aggregate:orderBy` - can we order by aggregates of this attribute (column, property)?
- `sum:attribute:aggregate:orderBy` - can we order by 'sum' aggregates of this attribute (column, property)?
- `sum:attribute:aggregate:orderBy` - can we order by sum aggregates of this attribute (column, property)?
- `filterBy` - can we filter by this thing (e.g. column, table, etc)?
- `proc:filterBy` - can we filter by the result of this proc (function resource)
- `attribute:filterBy` - can we filter by this attribute (column, property)?
- `condition:attribute:filterBy` - can we filter by this attribute (column, property) in the `condition` argument?
- `attribute:aggregate:filterBy` - can we filter by the aggregate of this attribute (column, property)?
- `sum:attribute:aggregate:filterBy` - can we filter by the 'sum' aggregate of this attribute (column, property)?
- `sum:attribute:aggregate:filterBy` - can we filter by the sum aggregate of this attribute (column, property)?
- `resource:aggregates:filterBy` - can we filter (a different resource) by this resource's aggregates?
- `sum:resource:aggregates:filterBy` - can we filter (a different resource) by this resource's 'sum' aggregates?
- `sum:resource:aggregates:filterBy` - can we filter (a different resource) by this resource's sum aggregates?
- `single` - can we get just one?
- `query:resource:single` - can we get a single one of these (resource) at the root?
- `singularRelation:resource:single` - can we get a single one of these (resource) from a
Expand All @@ -222,7 +222,7 @@ PostGraphile/graphile-build/graphile-build-pg plugins utilise:
- `manyRelation:resource:list`
- `manyRelation:resource:connection`
- `manyRelation:aggregates:orderBy` - can we order by aggregates of this manyRelation?
- `sum:manyRelation:aggregates:orderBy` - can we order by 'sum' aggregates of this manyRelation?
- `sum:manyRelation:aggregates:orderBy` - can we order by sum aggregates of this manyRelation?
- `jwt` - should the given codec behave as if it were a JWT?
- `insert:input:record` - input to the 'insert' mutation
- `totalCount` - on a codec, should we add the `totalCount` field?
Expand Down
22 changes: 11 additions & 11 deletions postgraphile/website/postgraphile/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ title: Best Practices
# PostGraphile Best Practices

This guide is a work in progress. If you have ideas for best practices, please
use the "Suggest improvements to this page" link above to submit them, or
use the Suggest improvements to this page link to submit them, or
discuss them in #documentation on
[the Graphile Discord chat](http://discord.gg/graphile).

### Foreign Key Indexes

PostgreSQL does _NOT_ add indexes to foreign keys by default. This isn't an
PostgreSQL does _NOT_ add indexes to foreign keys by default. This isnt an
issue for the forward relation (getting the record that your record belongs
to), but for the reverse relation (getting all the records that belong to your
record) it can make the lookup very expensive. Always add indexes to your
Expand All @@ -24,19 +24,19 @@ create table things (id serial primary key, user_id int not null references user
create index on things (user_id);
```

Out of the box, if you don't do this then the "reverse relation" will not
Out of the box, if you dont do this then the reverse relation will not
appear in your GraphQL schema. You can force it to appear by giving the
foreign key constraint the `+select` behavior, or you can disable this behavior
by adding `disablePlugins: ['PgIndexBehaviorsPlugin']` to your configuration.

### Row Level Security

If you're using RLS, it's best to enable it on every table in your database.
You should at least enable it on every table in your exposed schemas. It's
better to enable RLS and create a policy with `using (true)` to say "anything
goes" than to not enable RLS; this helps your team mates understand intent:
when you enable RLS you're being explicit about what access is allowed, whereas
if you don't you're just implicitly allowing all access, which could have been
If youre using RLS, its best to enable it on every table in your database.
You should at least enable it on every table in your exposed schemas. Its
better to enable RLS and create a policy with `using (true)` to say anything
goes than to not enable RLS; this helps your team mates understand intent:
when you enable RLS youre being explicit about what access is allowed, whereas
if you dont youre just implicitly allowing all access, which could have been
an oversight.

### Use Table GRANT for SELECT/DELETE and Column GRANT for INSERT/UPDATE
Expand Down Expand Up @@ -81,8 +81,8 @@ explicitness that should come from such operations.
You can get a leg up on this
[using `@graphile/simplify-inflection`](https://npmjs.com/package/@graphile/simplify-inflection).
The long names PostGraphile uses by default are to try and avoid people getting
naming conflicts when they run PostGraphile for the first time. Once you're more
comfortable you should move to using shorter names as it's a GraphQL best
naming conflicts when they run PostGraphile for the first time. Once youre more
comfortable you should move to using shorter names as its a GraphQL best
practice.

### Protect Your API
Expand Down
Loading

0 comments on commit 92ee5af

Please sign in to comment.