13

I have a web page with wizard format. The submission button to the API will be in the 4th step of wizard. However I want the data entered to be stored in database before moving to the next step in the wizard. I also want the REST API to be working for the pages having single tab.

So I designed the API to take a query parameter action = draft or submit. If action is draft, only certain fields are mandatory. If action is submit, all fields are mandatory. Validations in the service layer of REST API will be done based on the query parameter. Looks like I've to explicitly specify the if/else clauses in the documentation. Is this an acceptable form of RESTful design ? What would be best design with these requirements ?

3
  • 3
    Why does the interim data need to be stored in the DB?
    – Dan1701
    Commented Feb 10, 2016 at 4:42
  • 2
    @Dan1701: so you can resume the wizard from another machine. When filling long, complex forms, it may take a few days to complete all the required data, as the user may not have all the necessary data ready in hand, or the user may need to gather additional files to upload from different places. If you can resume from different device, you can load the wizard to upload a photo from mobile phone, and continue typing a long description/argument with a real keyboards on desktop, etc.
    – Lie Ryan
    Commented Feb 10, 2016 at 12:02
  • In that case, I think @guillaume31's answer makes sense.
    – Dan1701
    Commented Feb 11, 2016 at 1:07

3 Answers 3

8

Since you want to persist things on the server between wizard steps, it seems perfectly acceptable to consider each step as a separate resource. Something along these lines :

POST /wizard/123/step1
POST /wizard/123/step2
POST /wizard/123/step3

By including hypermedia links in the response, you can inform the client on what it can do after this step - go forward or back for intermediary steps, and nothing for the final step. You can see an example of it in Figure 5 here.

3
  • I'm am using Angular for the UI. So I'm not sure how much helpful the state machine is. But I think step based resource seems to be more meaningful than managing another table. Also, I should be able to submit everything in a single step. Will give it a shot on this design today. Thanks for the help.
    – TechCrunch
    Commented Feb 10, 2016 at 16:18
  • You're welcome. By the way, the "two table" approach isn't mutually exclusive with this. Having one HTTP resource per step doesn't dictate your object model on the application server, let alone the database schema. It's just a Web representation. Commented Feb 10, 2016 at 21:37
  • 1
    @TechCrunch Basically Guillaume means that the object/table representing the form can be broken into parts, where part of the model is saved at each step. In fact, you can just have each "step" be a form for part of the entire model. And if you take this approach, it actually makes the architecture incredibly simple. Each POST to the server will (create or) update the same model, and each GET will load the same model, and each step will be a form to fill in sets of fields that are semantically meaningful (belong together). And simply have a boolean on the model for in_progress or draft. Commented Feb 11, 2016 at 4:37
3

I had needed to do something similar some time ago, and the following describes what we end up with.

We have two tables, Item and UnfinishedItem. When the user fills in the data with the wizard, the data are stored in UnfinishedItem table. At each wizard step, the server validates the data entered during that step. When the user is finished with the wizard, the wizard renders a hidden/read-only form in a confirmation page that shows all data to be submitted. The user can review this page and go back to the relevant step to fix errors. Once the user are satisfied with their entries, the user clicks submit and the wizard then submits all data in the hidden/read only form fields to the API server. When the API server processes this request, it reruns all validations that it did during each step of the wizard, and performs additional validations that doesn't fit into the individual steps (e.g. global validations, expensive validations). After all validations are completed, the API server creates an entry in the Item table.

The advantages of two table approach:

  • in the database, you can have tighter constraints on the Item table than the UnfinishedItem table; you don't have to have optional columns that will actually be required when the wizard is finished.

  • Aggregate queries across the finished Items for reporting are easier as you don't have to remember to exclude the UnfinishedItems. In our case, we never needed to do aggregate queries between Item and UnfinishedItems, so this isn't a problem.

The disadvantage:

  • It's prone to duplication of the validation logic. The web framework we used, Django, makes this a bit more bearable as we used model inheritance with a bit of meta magic to change the constraints that we need to be different in Item and UnfinishedItem. Django generates most of the database and form validation from the model, and we only need to hack in a few additional validations on top of it.

Other possibilities I've considered and why we didn't go with them:

  • saving the data in cookies or local storage: user can't continue their submission from a different device or if they delete their browser history
  • store the UnfinishedItem as unstructured data (e.g. JSON) on the database or secondary datastore: I'll have to define parsing logic and can't use Django's automatic model/form validation.
  • do the per step validation on the client side: I'll have to duplicate the validation logic between Python/Django and JavaScript.
1
  • 1
    +1 for pointing out validations on 'draft'-type models and 'finished' models; I didn't think of that, and it's an important point that should be taken into consideration. Otherwise you'd probably have a bunch of if statements checking for draft status all throughout your validations, which would be just not good. Although some very sophisticated frameworks like Ruby on Rails could significantly simplify that problem if implemented correctly. Commented Feb 11, 2016 at 4:43
2

I've implemented this in a way similar to a mix of @guillauma31 and @Lie Ryan's solutions.

Here are the key concepts:

  1. There is a 3 step wizard that may be partially persisted until complete.
  2. Each step has it's own resource (eg.: /users/:id_user/profile/step_1, .../step_2, etc.)
  3. On each step, the data and completition status can be retrieved through GET requests and persisted through PATCH requests.
  4. Each resource has it's own validation rules to the entered data.
  5. Each step returns a key that have to be used in the input of the next step to guarantee the sequence. Once used or a new one is generated, this token expires.
  6. On the final step, we have all needed data on the database and a confirmation screen is displayed. This confirmation calls another resource to mark the data as complete (eg.: .../profile/confirm). This resource doesn't need to receive all data again. It only marks the data as correct and complete.
  7. There is a scheduled routine that wipes this incomplete entries that have more than a few days.

The front-end guys have to take care of the tokens to get the back and forth flow of the wizard working.

The API is stateless and atomic.

To make a "one step wizard" work with this setup you would have to change some things, like removing the token flow or creating a resource to return tokens based on the type of wizard or even create a new resource only to fill this specific single step wizard (like PUT /users/:id_user/profile/).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.