Intermediate level · Module 4: Back-End Basics with Node.js and Express · Lesson 3 of 3
Validating Input and Handling Errors
Validate every request on the server, respond with clear errors and keep secrets in environment variables.
In this lesson you will
- Validate request bodies and return 400 with field errors
- Keep configuration and secrets in environment variables
Key concepts
server-side validationerror response formatenvironment variablesnever trust the client
The lesson
Anyone can send requests to your API without your front end, so the server must validate every field: required values, types, lengths and allowed options. Normalise input first, for example by trimming spaces.
Return 400 with a consistent JSON body listing the problems, such as [{ field: "phone", message: "…" }], so the front end can show messages next to the right fields. Never return stack traces or internal details.
Keep settings and secrets — database passwords, API keys — in environment variables, loaded from a .env file in development that is listed in .gitignore and never committed.
Step by step
- Write a validation function for enquiries.
- Return 400 with field errors for invalid input.
- Test valid and invalid requests with a REST client.
- Move the port and any secrets into environment variables.
Validating a form on the server
Check a course enquiry on the server and return field-specific errors the front end can display.
// Server-side validation for a course enquiry form. Never trust the browser.function validateEnquiry(body) { const errors = []; const name = typeof body.name === "string" ? body.name.trim() : ""; const phone = typeof body.phone === "string" ? body.phone.replace(/\s+/g, "") : ""; const course = body.course; if (name.length < 2 || name.length > 80) errors.push({ field: "name", message: "Enter your full name (2–80 characters)." }); if (!/^\+?211\d{9}$|^0\d{9}$/.test(phone)) errors.push({ field: "phone", message: "Enter a South Sudan number, e.g. 0912345678." }); if (!["web-development", "graphic-design", "ms-office"].includes(course)) errors.push({ field: "course", message: "Choose a course from the list." }); return errors.length ? { ok: false, errors } : { ok: true, value: { name, phone, course } };} const tests = [ { name: " Nyandeng Akol ", phone: "0912 345 678", course: "web-development" }, { name: "A", phone: "12345", course: "piano" },];for (const body of tests) console.log(JSON.stringify(validateEnquiry(body)));
Output
{"ok":true,"value":{"name":"Nyandeng Akol","phone":"0912345678","course":"web-development"}}
{"ok":false,"errors":[{"field":"name","message":"Enter your full name (2–80 characters)."},{"field":"phone","message":"Enter a South Sudan number, e.g. 0912345678."},{"field":"course","message":"Choose a course from the list."}]}How it works
The function never trusts its input: it checks types, trims the name, removes spaces from the phone number and only accepts courses from a fixed list. The phone pattern accepts +211 followed by nine digits or a 10-digit number starting with 0. Instead of stopping at the first problem, it collects every error with the field it belongs to, so a front end can show each message next to the right input. In an Express route, the result decides between a 201 response and a 400 response with the errors. Output produced by running the code with Node.js.
Key concepts
never trust the clientnormalising inputallow-listsfield-specific error objects
Practice activity
Implement and test the enquiry endpoint with full validation.
Expected result: Valid enquiries are stored with 201; invalid ones return 400 with field-specific messages.
Check your understanding
-
Why must the server validate input if the form already does?
Show answer
Requests can be sent directly to the server, bypassing the form.
-
Where should API keys be kept?
Show answer
In environment variables, never in source code or the repository.
Taught live by an instructor — online or in person. Free course lessons are for self-study and do not lead to a certificate — see registration options.