Skip to main content

Loading a Form's Structure

Overview

This endpoint delivers the form "blueprint" that the client-web form page renders: the full list of questions with their field types, validation rules, conditional logic, theme, and form-level settings such as requireLineLogin, oneTimeSubmission, convertToMember, profileMapping, and thankYou.

Two helper endpoints sit alongside it: one that checks whether the current user has already submitted the form (for single-submission forms), and a stub that does nothing but was ported for parity.

Business Flow

GET /api/form-builder/:hash

  1. Look up the active form_builder by form_hash. If none is found, return 400 with the message Bad Request — the source throws new BadRequestException() with no message, so NestJS supplies the reason phrase.
  2. transformCover — if theme.cover is a non-empty string, replace it with the file's public URL, but only after confirming the object actually exists via IsFileExist. A missing file or missing storage writes null instead. The encoder preserves the original key order of the theme object and changes only the cover value.
  3. transformPattern — load the active common rules without the cache using FindAllActive. For each question whose commonRuleId matches a rule, append a commonRule key holding rule.properties to that question object (mirroring the JS spread {...question, commonRule}). Matching imitates Number(rule.id) === Number(question.commonRuleId): numeric strings match, an empty string yields 0, and anything that coerces to NaN matches nothing.
  4. Return the entire FormBuilder entity as JSON.

POST /api/form-builder/:hash/is-submitted

  1. Load the form through the flow above (400 if absent) to obtain line_oa_id.
  2. The body is not trusted — any lineUserId the client sends is discarded entirely; identity comes only from verifying x-liff-token. Trusting the body would let anyone probe which LINE user has already submitted the form, turning it into a per-user oracle.
  3. If verification fails or no token is present, return false rather than an error. Real duplicate prevention still happens at submit time.
  4. Once verified, query form_submission for a row with is_submitted = true for the (form_hash, line_user_id) pair.
  5. The response is a primitive boolean rendered as text"true" or "false" — with status 201, the NestJS default for @Post.

POST /api/form-builder/:hash/form-submission

A debug stub in the source with its real body commented out. It echoes :hash back as a raw response body with status 201, ported purely for parity.

Key Files & Functions

RouteHandler
GET /api/form-builder/:hashinternal/formbuilder/handler.go(*Handler).GetByHash
POST /api/form-builder/:hash/is-submitted(*Handler).IsSubmitted
POST /api/form-builder/:hash/form-submission(*Handler).CheckFormSubmission (stub)
  • internal/formbuilder/register.goRegister(r, deps) wires up the repository, an uncached formbuilderrule.Service, storagex, and liff.Service
  • internal/formbuilder/service.goGetByHash, IsSubmitted, transformCover, transformPattern, getImageURL, matchRule, jsNumber, extractLineUserID
  • internal/formbuilder/repository.goFindActiveByHash, IsSubmitted
  • internal/formbuilder/entity.goFormBuilder, RawJSON, and decodeOrdered, an ordered JSON object used to preserve key order

Connections to Other Services

  • The form_builder table (jsonb columns questions, theme, profile_mapping, thank_you, field_attribute_mappings) and the form_submission table.
  • Central Validation Rules is the source of commonRule.
  • LIFF Token Verification verifies x-liff-token for the is-submitted endpoint.
  • internal/storagex provides IsFileExist and GetPublicURL for handling theme.cover.
  • Consumed by Form Answer Submission and OTP Verification, both of which call GetByHash as the first step of their pipeline.
  • The related client-web feature is form-fill.