Merge pull request #466 from diogob/adds_multiple_insert_section
Adds section about multiple insertion/update to api/writing docs [#405]
This commit is contained in:
@@ -71,6 +71,93 @@ returns something like
|
||||
[ { "id": 1 }, { "id": 2 } ]
|
||||
```
|
||||
|
||||
### Multiple Tables Insertion or Update
|
||||
|
||||
The cleanest way to insert or update data into multiple tables using only one POST/PATCH request
|
||||
is to create a view that will join all target tables and present a single endpoint.
|
||||
In our example let's assume one users table and one companies table.
|
||||
In this case, we want a signup endpoint to create the first user within a company.
|
||||
And for this endpoint we want to insert with one request both user and company.
|
||||
|
||||
```SQL
|
||||
CREATE TABLE companies (
|
||||
id serial primary key,
|
||||
name text unique
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id serial primary key,
|
||||
name text not null,
|
||||
pass text,
|
||||
company_id integer not null references companies
|
||||
);
|
||||
```
|
||||
|
||||
Having both tables created we create a view that joins them to be used
|
||||
as a ```/signup``` endpoint.
|
||||
|
||||
```SQL
|
||||
CREATE VIEW signup AS
|
||||
SELECT
|
||||
c.name AS company_name,
|
||||
u.name AS user_name,
|
||||
u.pass
|
||||
FROM
|
||||
public.users u
|
||||
JOIN public.companies c ON c.id = u.company_id;
|
||||
|
||||
```
|
||||
|
||||
After the signup view creation, we can issue ```GET``` requests to read data
|
||||
from users and companies, but any atempt to ```POST``` or ```PATCH``` data will fail.
|
||||
PostgreSQL won't allow any data change on views that have a ```JOIN```
|
||||
clause in their ```FROM``` without a proper ```INSTEAD OF``` trigger.
|
||||
So in the example bellow we create a trigger to allow insertion of data in the signup view.
|
||||
The trigger is a simple PL/pgSQL function that first inserts into the companies table and
|
||||
uses the newly create company_id to create its first user.
|
||||
|
||||
|
||||
```SQL
|
||||
CREATE FUNCTION signup()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
vcompany_id int;
|
||||
BEGIN
|
||||
INSERT INTO companies (name) VALUES (new.company_name) RETURNING id INTO vcompany_id;
|
||||
INSERT INTO users (name, pass, company_id) VALUES (new.user_name, new.pass, vcompany_id);
|
||||
RETURN new;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER signup
|
||||
INSTEAD OF INSERT ON signup
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE signup();
|
||||
```
|
||||
|
||||
After the trigger creation we can issue a normal ```POST``` request to our signup endpoint:
|
||||
|
||||
```HTTP
|
||||
POST /signup
|
||||
{ "company_name": "foo", "user_name": "bar" }
|
||||
```
|
||||
|
||||
For an endpoint such as signup its usually not desirable to have a ```PATCH``` route for updates,
|
||||
and we will skip this example for the sake of brevity. But it would be implemented in a very
|
||||
similar way to our ```POST``` example.
|
||||
|
||||
<div class="admonition note">
|
||||
<p class="admonition-title">Design Consideration</p>
|
||||
|
||||
<p>It's advisable to create a separate trigger for <code>UPDATE</code> and <code>INSERT</code>
|
||||
avoiding conditionals that decide which is the trigger current operation.
|
||||
This makes it easier to change code for (or even disable) one operation without intefering with others while
|
||||
improving readability.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
### Bulk Updates
|
||||
|
||||
* ❌ Cannot be cached or prefetched
|
||||
|
||||
Reference in New Issue
Block a user