I am building an API that (I am deliberately simplifying the schema below to only focus on what is questionable):
I have a table that roughly looks like this:
CREATE TABLE IF NOT EXISTS some_table
(
id INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL PRIMARY KEY,
user_ids TEXT[] NOT NULL,
field_1 TEXT NOT NULL,
field_2 TEXT NOT NULL,
field_3 TEXT NOT NULL,
hash_id TEXT GENERATED ALWAYS AS ( MD5(field_1 || field_2 || field_3)) STORED UNIQUE NOT NULL
)
The API is a bit trickier than a conventional CRUD in that:
(1) Inserting into the table depends on whether md5(field_1||field_2||field_3)
already exists. If it does, I need to append the value user_id
to the array field user_ids
. Else, insert the row.
(2) Deleting a row also depends on the state of user_ids
. Actually, my current implementation makes the database handle deletions in that there is a trigger that acts on updates and deletes rows whenever cardinality(user_ids) = 0
.
CREATE OR REPLACE FUNCTION delete_row() RETURNS trigger AS
$$
BEGIN
IF tg_op = 'UPDATE' THEN
DELETE FROM some_table WHERE id = NEW.id;
RETURN NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER some_table_delete_row
AFTER UPDATE
ON some_table
FOR EACH ROW
WHEN (CARDINALITY(NEW.user_ids) = 0)
EXECUTE PROCEDURE delete_row();
As you can see, there is no traditional deleting. What really happens is removing items from user_ids
until the length of the array is 0 and then the database will autoremove the row.
I think that the
PUT
method is the best match for how I want to effectively implement upserts.It's trickier with
DELETE/PUT
for decrementinguser_ids
. Ideally, that looks likesPATCH
in that only one field is modified at a time and nothing is allowed to be deleted manually.Using an auto-generated
hash_id
value is convenient. That said, I am not sure whether it's the best option when I think of how deletes should work. The endpoint for that isbase_url/items/{hash_id}
, but in this case I will also need to calculate the hashed value in code or, as another option, just always pass the object in the request so that I can doWHERE hash_id = md5($field_1 || $field_2 || $field_3)
.
What do you think?