Development
Full Stack + AI Development Program
A full-stack feature, plus a careful place for an AI step inside it.
Full Stack + AI Development Program combines a full-stack feature with one generative step that has a boundary. You will build the ordinary path first — screen, API, and stored data — then add an AI assist that a person can accept or discard. The course assumes web development experience.
Course syllabus
01. A note you can save
- The feature without AI
Create a note with a title and a body, and read it back from stored data.
- The screens in this half
A list, a create form, and a page for one note.
- The routes in this half
GET the list, POST a note, and GET one by id.
- The fields in this half
Title, body, and createdAt, with no summary yet.
- Done means a refresh still shows it
The note is on the list because it was stored.
- What this half does not include
No model call, no generated text, and no provider key.
- Accounts can wait
The create-and-read path cannot wait.
- The path in one line
Form, POST, document, GET, screen.
02. Screens for create and read
- A form with title and body
Both are controlled inputs.
- Submit posts JSON
The handler sends the fields to POST /notes and waits.
- The list fetches on load
GET /notes fills an array in state.
- The list shows the title and a short start of the body
The full body waits on the detail page.
- The id from the server is the link
The list does not invent a key.
- The detail page loads that id
It shows the stored title and body.
- The empty list has a sentence
No notes yet is a state you wrote on purpose.
- These screens have no model button
Create and read are finished before any draft exists.
03. The notes API
- The server parses JSON
A JSON body comes in, and JSON goes out.
- POST /notes inserts one document
It returns that document with status 201.
- GET /notes returns an array
Newest first, from the collection.
- GET /notes/:id returns one note or 404
A missing document is not an empty object.
- The three handlers live in one router
List, create, and read, attached together.
- Create and read with curl first
The API works before you trust the form.
- Status codes for this API
201 for create, 200 for reads, 400 for a bad body, and 404 for a missing note.
- POST does not call another service
It validates and inserts, and that is the whole handler.
04. Documents in the collection
- The database URI is read on the server
It comes from the API environment, not from the browser.
- One collection, one shape
Title, body, and createdAt.
- Insert only the checked fields
Plus the time the server sets.
- Read the title back in the shell
The document matches the POST.
- The URL id is the ObjectId as text
The detail route uses that value.
- A value that is not an ObjectId is 400
It fails before a lookup, and the error is JSON.
- Sort newest first
createdAt descending, so the note you just saved is at the top.
- This document has no summary field
You add that only in the later half.
05. Validation and error text
- The title has to be trimmed text
Empty and over the limit both fail.
- The body has to be non-empty text
A note with no body is not stored.
- 400 names the field that failed
The response says title or body.
- The form shows that server message
It appears by the field, from the response body.
- A dropped network call keeps the typed text
The person does not lose the note because the request failed.
- The detail page explains a 404
This note does not exist.
- A 500 on screen is generic
The server log keeps the stack.
- A hand-built request still hits the check
Skipping the form does not skip validation.
06. Prove the plain path
- Create a note and refresh the list
The row comes from GET, not from form state.
- Open it from the list
The body matches what you typed.
- A blank title inserts nothing
The collection does not gain a document.
- A bad id does not leak a cast error
The client gets 400 JSON, not a library page.
- Search the bundle for the database URI
It is absent.
- Name the files in this half
Router, database helper, list, form, and detail.
- Demo create and read with the model code absent
The path still makes sense if that file does not exist.
- Stop before you add a side effect to insert
The next work is a new route, not a hidden call inside POST.
07. Where a draft is allowed
- Where an AI step is allowed
After a note exists, a person may ask for a one-sentence draft summary.
- POST /notes still does not call a model
Saving stays a plain insert.
- A button on the detail page
Suggest a summary, and it is optional.
- The server reads the stored body
The browser does not get to replace the source of the draft.
- The model result is a draft
It is shown to the person, and it is not written yet.
- The draft may only compress the body
The fixed instructions say not to add claims.
- A person is the step after the draft
Nothing is saved until they accept or edit.
- No chat box and no second model
This product has one generative step.
08. The provider key
- The key authorizes the provider
It is a secret for the outgoing call.
- The API process reads it from the environment
A variable on the server, present at startup.
- The React bundle must not contain the key
No client env prefix, and no prop that carries it.
- The sample env file has the name only
The value stays off the repository.
- The browser calls your draft route
POST /notes/:id/summary-draft hits the API you wrote.
- The server attaches the key on the way out
The provider request is built on the server.
- A missing key is a generic 500
The log says the variable is unset, and the page does not.
- Rotation changes the server env
The client never knew the old value or the new one.
09. The server calls the model
- Calling a model from the server
A function takes the stored body and returns text, or fails.
- The prompt lives in that function
One sentence, only what the body says, and no extra claims.
- The body is passed as source text
It is data for the summary, not a new set of instructions.
- The call has a timeout
A millisecond limit, and past that the function fails.
- A result that is not text is a failure
You do not guess a summary out of an unexpected shape.
- Do not log the outgoing key
Log the note id and the outcome, not the authorization header.
- The button never calls the provider
React only hits your route.
- 200 returns a draft and leaves the note alone
The document is unchanged until a later save.
10. An unsaved draft on the page
- The button enters a loading state
The person can see that the draft request is running.
- Show the draft in its own box
Separate from the saved title and body.
- The stored note does not change when a draft arrives
Title and body stay as they were.
- Three actions under the draft
Accept, edit, and discard.
- Discard closes the box
No write, and the document stays as it was.
- Edit copies the draft into a field
The person changes the words before any save.
- Accept and edit use a save route
They do not reuse the model route.
- Ask again and the unsaved box updates
A newer draft replaces the old one, and it still does not write.
11. Saving only confirmed text
- Accept, edit, or discard
Accept stores the draft, edit stores the revision, and discard stores nothing.
- PATCH /notes/:id/summary writes the text
The field is set from the words the person confirmed.
- The summary is checked like any other field
Plain text, under a length cap.
- An empty summary is a real value
Clearing it stores an empty string on purpose.
- The draft route never writes
It only returns a draft.
- The list shows a summary only after it is stored
An unsaved box does not appear as data.
- Reload shows a summary that was patched
It is on the document, not only in the tab.
- Discard, reload, and the summary is absent
Memory in the page was not a save.
12. Timeouts and bad model output
- Timeouts and bad output
The call can hang, return junk, or add facts that are not in the note.
- A timeout becomes a sentence on the page
The draft took too long, the person may try again, and the note is unchanged.
- A provider error becomes a 502
Your route says the draft failed, in a short sentence.
- Empty model text is a failure
You do not offer a blank draft as a success.
- Pick one rule for a draft that is not one sentence
Show it for review, or reject it, and then keep that rule.
- Tell the person to compare the draft to the body
Extra facts are their call to delete, and save still waits.
- Retry once at most
Then stop and show the error.
- A failed draft does not block GET
The note page still loads.
13. Access, payment, and untrusted text
- The model does not decide access
Your route decides who can open the note, before any model call.
- The model does not decide payment
A price, a refund, or a plan is not an output you obey.
- Do not ask the model who is allowed
Permission is a query you wrote, not a prompt.
- A missing note is 404 before the call
You load the document first, and you skip the model if it is absent.
- Create and read work if the button is never used
The generative step is optional.
- A flag can turn off only the draft route
CRUD stays up, and the draft route returns 404.
- Render the draft as text
Not as HTML.
- Do not execute the draft
It is words to read, not an instruction to the server.
14. The summary field
- Add summary as a string
Default empty, on the note document.
- Older notes still load
A missing summary reads as empty.
- PATCH enforces a maximum length
A paste cannot store an unbounded string.
- PATCH does not call the model
It stores the checked string it was given.
- The read rule is the PATCH rule
Whoever may open the note may update its summary, and the model is not in that rule.
- Label the saved summary on the page
Under the body, as the summary, not as the body.
- The box says unsaved until PATCH succeeds
The person can tell draft from stored text.
- No stored summary means no summary block
The page simply omits it.
15. Screen states for the draft
- Idle is the button and no box
Nothing has been requested.
- Loading disables the button
A line says the draft is in progress.
- A ready draft shows the three actions
The text is visible, and it is not saved yet.
- A save in flight does not send twice
Accept or edit stays disabled until the PATCH returns.
- The summary block updates from the PATCH body
The page uses the stored value, not a guess.
- A failed draft shows the error line
The body of the note is untouched.
- Edit mode is a textarea prefilled with the draft
Save sends that text to PATCH.
- Discard does not call PATCH
The box closes and the document is the same.
16. Logs that omit the key
- Log the note id and the outcome
Started, succeeded, timed out, or provider error.
- Never log the key
Including headers on the outgoing request.
- Log a length instead of the whole body
The note may be private, and the size is enough.
- A draft failure is not a create failure
The log line names the draft route.
- Put one request id on the draft call
You can find that timeout later.
- The browser gets the short error
The log can hold more, and the page does not.
- Read one failed draft in the log
Say whether it timed out or the provider failed.
- A stack that prints the key is a bug
You change the logging so the secret is not in the trace.
17. Limits on the draft route
- Refuse a body over the draft limit
The route stops before the model call.
- Cap the summary on PATCH
The same class of limit, on the stored field.
- Limit how often the draft route may run
A small number per minute, so a loop cannot keep calling.
- POST /notes is not under that same tight cap
Saving a note stays the ordinary path.
- The timeout is one named constant
The call uses that value, and not a second copy.
- The prompt is one function
The route does not carry a second copy of the instructions.
- The cap is there because calls cost money
You can say why, without putting a price on the screen.
- The person sees too long or too many tries
A clear refusal, not a generic crash.
18. Walk both paths
- Create is form, POST, document, list
You can point at each hop.
- Read is list, click, GET, body
The page shows the stored note.
- Draft is button, server read, model, page
The key stays on the server in that hop.
- Accept is PATCH, then a reload that still shows it
The summary was written.
- Discard is no PATCH
A reload shows no summary.
- A timeout leaves the stored note as it was
The page says the draft failed.
- Say where the key is
The server environment, and not the client bundle.
- Two paths, two sentences
Saving a note never calls a model, and a summary is stored only after a person confirms it.
19. Files in order
- Server entry and its environment
Port, database URI, and the model key.
- The notes router
List, create, read, summary draft, and summary patch.
- The validation module
Title, body, and summary.
- The model function
Prompt, timeout, and a string or a failure.
- The list and the form import no model
Those screens only talk to the plain notes API.
- The detail screen owns the draft actions
Fetch, the button, accept, edit, discard, and the summary block.
- Nothing in the client bundle imports the key
You can search and show that it is absent.
- Remove the model function and create still works
Read still works too.
20. Explain the two paths
- Explaining the system
A note is stored by an ordinary API, and a summary is stored only after a person confirms it.
- How you know the key is server-side
The client calls your route, and the bundle does not contain the secret.
- What the model is allowed to do
Offer one sentence, and not save it, and not grant access.
- What the person must do
Accept, edit, or discard before any summary exists on the document.
- How you explain a timeout
The note stays, and the screen says the draft failed.
- How you explain bad output
Untrusted text, compared with the body, and never executed.
- Access and payment in this system
Neither one is a model decision.
- The order of a demo
Create and read, then a draft, then a discard.
Lecture videos stay with the course. Playback for enrolled students will open once payments are available. Video links are not published on this page.
