ruby 40 lines · 8 steps

Idempotent order creation in Rails

A unique database index plus a rescue handler turns duplicate order submissions into a safe, single-order outcome.

Explained by highlit
1class OrdersController < ApplicationController
2 rescue_from ActiveRecord::RecordNotUnique, with: :handle_duplicate_submission
3 
4 def create
5 @order = current_user.orders.build(order_params)
6 @order.idempotency_key = params[:idempotency_key]
7 
8 if @order.save
9 OrderConfirmationJob.perform_later(@order.id)
10 redirect_to @order, notice: "Your order has been placed."
11 else
12 render :new, status: :unprocessable_entity
13 end
14 end
15 
16 private
17 
18 def order_params
19 params.require(:order).permit(:shipping_address, :billing_address, line_items_attributes: %i[product_id quantity])
20 end
21 
22 def handle_duplicate_submission
23 existing = current_user.orders.find_by(idempotency_key: params[:idempotency_key])
24 redirect_to existing, notice: "This order was already submitted."
25 end
26end
27 
28class CreateOrders < ActiveRecord::Migration[7.1]
29 def change
30 create_table :orders do |t|
31 t.references :user, null: false, foreign_key: true
32 t.string :idempotency_key, null: false
33 t.string :shipping_address
34 t.string :billing_address
35 t.timestamps
36 end
37 
38 add_index :orders, %i[user_id idempotency_key], unique: true
39 end
40end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Enforcing idempotency at the database level with a unique index is more reliable than checking in application code.
  2. 2Rescuing the constraint violation lets you convert a race-lost insert into a graceful redirect to the existing record.
  3. 3Pairing a unique key with a controller handler makes duplicate form submissions harmless without extra request state.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Idempotent order creation in Rails — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code