I am building an API on top of my PostgreSQL database, using Hasura and GraphQL as the middle layer.
I run a service business, where customers can subscribe to services such as "weekly cleaning of the coffee machine" or "refilling of the mojito cabinet". A customer can only subscribe to the same service once.
We are a sales driven organization,
so we have different apps where people can add (customer, subscription)
combination.
We want an end point which accepts this (customer, subscription)
combo, and takes care of details like inserting customer
if it does not exist.
We first tried using the following mutation against Hasura:
mutation InsertOrders {
insert_orders(objects: [
{
customer: {data: {name: "GE"}},
subscription: {data: {name: "Coffee Refill"}}
}
])
{
affected_rows
}
}
This operation only succeeds if "GE"
is not an existing customer and "Coffee Refill"
is not an existing subscription.
We then tried using the "on_conflict"
functionality in Hasura:
mutation InsertOrders {
insert_orders(objects: [
{
customer: {data: {name: "GE"},
on_conflict: { constraint:customers_name_unique , update_columns:[name] }
},
subscription: {data: {name: "Coffee Refill"},
on_conflict: { constraint:subscriptions_name_unique , update_columns:[name] }
}
}
])
{
affected_rows
}
}
This kind of works, but it has several drawbacks:
- It updates the customer (or subscription) row if the customer exists, effectively a no-op update. This also updates a potential
updated_at
column. - It's inefficient (see 1.). We have a tiny database (hundreds of rows), and our attempt to insert around 50
(customer, subscription)
combos in one request timed out.
Database definition is here:
CREATE TABLE customer (
id UUID PRIMARY KEY,
name VARCHAR(50) UNIQUE
);
CREATE TABLE subscription (
id UUID PRIMARY KEY,
name VARCHAR(50) UNIQUE
);
CREATE TABLE orders (
customer UUID REFERENCES customer (id),
subscription UUID REFERENCES subscription (id),
PRIMARY KEY (customer, subscription)
)