diff --git a/docs/api/writing.md b/docs/api/writing.md index 0c9473694..a1d2321ed 100644 --- a/docs/api/writing.md +++ b/docs/api/writing.md @@ -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. + +
Design Consideration
+ +It's advisable to create a separate trigger for UPDATE and INSERT
+ 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.
+