@@ -63,16 +63,26 @@ Create a file at `form/schema.ts`:
63
63
``` ts title:src/forms/form/schema.ts
64
64
import { z } from ' zod'
65
65
66
- export const SubmissionSchema = z .object ({
66
+ export const feedbackSchema = z .object ({
67
67
name: z .string (),
68
68
rating: z .number ().min (1 ).max (5 ),
69
69
feedback: z .string ().optional (),
70
70
})
71
71
72
- export type Submission = z .infer <typeof SubmissionSchema >
72
+ export const rsvpSchema = z .object ({
73
+ name: z .string (),
74
+ attending: z .boolean (),
75
+ guests: z .number ().int ().min (0 ),
76
+ })
77
+
78
+ export const formSchemas: Record <string , z .ZodObject <any >> = {
79
+ feedback: feedbackSchema ,
80
+ rsvp: rsvpSchema ,
81
+ }
73
82
```
74
83
75
- This schema ensures that any incoming submission contains a valid name, a 1–5 rating, and optional feedback.
84
+ Feedback Schema: Validates that submissions include a name (string), a rating (number between 1 and 5), and optional feedback (string).
85
+ RSVP Schema: Validates that submissions include a name (string), an attending status (boolean), and the number of guests (non-negative integer).
76
86
77
87
## Step 1: Define Resources
78
88
@@ -94,18 +104,28 @@ Create an API route that validates and stores form submissions, then triggers fu
94
104
``` ts title:services/forms.ts
95
105
import { api } from ' @nitric/sdk'
96
106
import { submissions , submitted } from ' ../resources/resources'
97
- import { SubmissionSchema } from ' ../form/schema'
107
+ import { formSchemas } from ' ../form/schema'
98
108
99
109
const formApi = api (' forms' )
100
110
101
111
formApi .post (' /forms/:formId' , async (ctx ) => {
102
112
const formId = ctx .req .params .formId
103
- const parsed = SubmissionSchema .safeParse (ctx .req .json ())
113
+ const schema = formSchemas [formId ]
114
+
115
+ if (! schema ) {
116
+ ctx .res .status = 400
117
+ ctx .res .json ({ msg: ` Unknown formId: ${formId } ` })
118
+ return
119
+ }
120
+
121
+ const parsed = schema .safeParse (ctx .req .json ())
122
+
104
123
if (! parsed .success ) {
105
124
ctx .res .status = 400
106
125
ctx .res .json ({ msg: ' Invalid submission' , errors: parsed .error .format () })
107
126
return
108
127
}
128
+
109
129
const data = parsed .data
110
130
const id = ` ${formId }-${Date .now ()} `
111
131
@@ -125,7 +145,7 @@ import { receipts, submissions, output } from '../resources/resources'
125
145
import { buildReceipt } from ' ../form/receipt'
126
146
127
147
receipts .subscribe (async (ctx ) => {
128
- const { id } = ctx .req .json ()
148
+ const { id, formId } = ctx .req .json ()
129
149
const submission = await submissions .get (id )
130
150
131
151
if (! submission ) {
@@ -134,7 +154,7 @@ receipts.subscribe(async (ctx) => {
134
154
}
135
155
136
156
// Build the PDF buffer from the submission data
137
- const buffer = await buildReceipt (submission )
157
+ const buffer = await buildReceipt (submission , formId )
138
158
139
159
// Store the PDF file in the bucket
140
160
const file = output .file (` ${id }.pdf ` )
@@ -148,45 +168,66 @@ receipts.subscribe(async (ctx) => {
148
168
149
169
``` ts title:form/receipt.ts
150
170
import PDFDocument from ' pdfkit'
151
- import { Submission } from ' ./schema'
171
+ import { feedbackSchema , rsvpSchema } from ' ./schema'
172
+ import { z } from ' zod'
173
+
174
+ type FeedbackSubmission = z .infer <typeof feedbackSchema >
175
+ type RsvpSubmission = z .infer <typeof rsvpSchema >
152
176
153
- export const buildReceipt = async (data : Submission ) => {
177
+ export const buildReceipt = async (
178
+ data : any ,
179
+ formId : string ,
180
+ ): Promise <Buffer > => {
154
181
const receipt = new PDFDocument ({ bufferPages: true })
155
182
156
183
const doneWriting = new Promise <Buffer >((resolve ) => {
157
184
const buffers: Uint8Array [] = []
158
185
159
- // Collect PDF output in memory as chunks
160
186
receipt .on (' data' , buffers .push .bind (buffers ))
161
187
receipt .on (' end' , () => {
162
188
const pdfData = Buffer .concat (buffers )
163
189
resolve (pdfData )
164
190
})
165
191
166
- const { name, rating, feedback } = data
167
-
168
- // Write header
169
192
receipt .font (' Times-Roman' ).fontSize (20 ).text (' Survey - Receipt' , 100 , 100 )
170
-
171
- // Write section title
172
193
receipt
173
194
.font (' Times-Roman' )
174
195
.fontSize (16 )
175
- .text (' Primary Applicant Details' , 100 , 150 )
196
+ .text (' Submission Details' , 100 , 150 )
176
197
177
- // Write submission details
178
- receipt
179
- .font (' Times-Roman' )
180
- .fontSize (12 )
181
- .text (
182
- ` Name: ${name }
198
+ if (formId === ' feedback' ) {
199
+ const { name, rating, feedback } = data as FeedbackSubmission
200
+
201
+ receipt
202
+ .font (' Times-Roman' )
203
+ .fontSize (12 )
204
+ .text (
205
+ ` Name: ${name }
183
206
Rating: ${rating }
184
207
Feedback: ${feedback || ' N/A' } ` ,
185
- 100 ,
186
- 175 ,
187
- )
208
+ 100 ,
209
+ 175 ,
210
+ )
211
+ } else if (formId === ' rsvp' ) {
212
+ const { name, attending, guests } = data as RsvpSubmission
213
+
214
+ receipt
215
+ .font (' Times-Roman' )
216
+ .fontSize (12 )
217
+ .text (
218
+ ` Name: ${name }
219
+ Attending: ${attending ? ' Yes' : ' No' }
220
+ Guests: ${guests } ` ,
221
+ 100 ,
222
+ 175 ,
223
+ )
224
+ } else {
225
+ receipt
226
+ .font (' Times-Roman' )
227
+ .fontSize (12 )
228
+ .text (' Unknown form type. No details available.' , 100 , 175 )
229
+ }
188
230
189
- // Finalize the PDF
190
231
receipt .end ()
191
232
})
192
233
@@ -206,7 +247,7 @@ import { receipts } from '../resources/resources'
206
247
receipts .subscribe (async (ctx ) => {
207
248
const { id } = ctx .req .json ()
208
249
209
- // Handle delivery or hook into a real email/SaaS integration
250
+ // Simulate delivery or hook into a real email/SaaS integration
210
251
console .log (` Delivering receipt for submission: ${id } ` )
211
252
})
212
253
```
@@ -239,6 +280,8 @@ nitric start
239
280
240
281
Use the dashboard to submit your survey data -
241
282
283
+ Form ID: feedback
284
+
242
285
``` json
243
286
{
244
287
"name" : " Jane" ,
@@ -247,10 +290,25 @@ Use the dashboard to submit your survey data -
247
290
}
248
291
```
249
292
293
+ Form ID: rsvp
294
+
295
+ ``` json
296
+ {
297
+ "name" : " Jane" ,
298
+ "attending" : true ,
299
+ "guests" : 5
300
+ }
301
+ ```
302
+
250
303
![ Submit Form] ( /docs/images/guides/survey-application/submit.png )
251
304
252
305
You should see messages in your console for PDF generation and delivery.
253
306
307
+ ``` bash
308
+ Delivering receipt for submission: rsvp-1744131025791
309
+ Receipt stored for rsvp-1744131025791
310
+ ```
311
+
254
312
### Get a Receipt
255
313
256
314
Replace ` <timestamp> ` with the value returned from the submission.
0 commit comments