DEV Community

Cover image for I Built a Contact Form API to Understand Express Request & Response Objects
Chinwuba
Chinwuba

Posted on

I Built a Contact Form API to Understand Express Request & Response Objects

I learn best by building. So instead of reading another article about Express, I built a small Contact Form API with two endpoints and forced myself to use req.body, req.params, req.query, and res.json() in a real context.

Here's what I built and what I actually learned.

The Setup
Two endpoints:

POST /contact — accepts name, email, message. Saves to an array. Returns the created entry.
GET /contacts — returns all submissions, with optional name filtering.

No database. Just an in-memory array. Simple enough to stay focused, real enough to surface actual problems.

req.body — The Envelope
When a client sends a POST request with JSON data, that data lives in req.body. But Express doesn't read it automatically — you have to tell it to:

app.use(express.json())
Without that line, req.body is undefined. I think of it as opening the envelope before reading the letter.

req.query — The Optional Extras
For the GET route, I added filtering by name. Someone hits /contacts?name=Jeffrey and gets back only their submission.

js
if (!req.query.name) {
  res.json(submissions)
} else {
  const found = submissions.filter(sub => sub.name === req.query.name)
  res.json(found)
}
Enter fullscreen mode Exit fullscreen mode

The key lesson: check for undefined, not "". When no query param is sent, req.query.name is undefined — not an empty string.

req.params — The URL Placeholder

I added a third endpoint — GET /contact/:id — to fetch a single submission. The :id is a named placeholder. Whatever the client puts there, Express captures it as req.params.id

One gotcha: req.params.id is always a string. My IDs were numbers. So I had to convert:

js
const id = Number(req.params.id)
const found = submissions.find(sub => sub.id === id)

if (!found) return res.status(404).json({ error: "Not found" })
res.json(found)
Enter fullscreen mode Exit fullscreen mode

Also — use find for single items, not filter. Filter always returns an array. Find returns the object itself.

res.status().json() — Talking Back Properly
Status codes are not optional decoration. They tell the client exactly what happened:

201 — something was created
200 — request succeeded
404 — resource doesn't exist
400 — the client sent bad data

js
res.status(201).json({
  message: "Submission received",
  data: newSubmission
})
Enter fullscreen mode Exit fullscreen mode

Wrapping your response in a { message, data } shape is a clean pattern that scales as your API grows.

What the In-Memory Array Taught Me
Every time the server restarted, the array wiped. Frustrating at first, clarifying after — it made the need for a database concrete, not abstract. You feel the limitation before you learn the solution. That's a better order than most tutorials give you.

If you're learning backend, build something broken first. It teaches faster than building something perfect.

Top comments (0)