Skip to content

Creating a Server and Handling Requests

With Express installed, let’s build a server that actually handles requests — understanding ports, HTTP fundamentals, and how to send proper responses.

When a client (browser, React app, curl) makes a request:

  1. It opens a TCP connection to your server’s IP address and port
  2. Sends an HTTP request: method + path + headers + optional body
  3. Your server handles the request and sends an HTTP response: status code + headers + body
  4. The connection closes (or is reused for keep-alive)

Your Express server listens on a port for incoming connections.

A port is a number that identifies a specific service on a machine. Common conventions:

  • 80 — HTTP (production web servers, no port shown in URLs)
  • 443 — HTTPS
  • 3000, 3001, 8080 — common development ports

In development, you access your API at http://localhost:3000. In production on Railway, the PORT is assigned dynamically via process.env.PORT.

const PORT = parseInt(process.env.PORT || '3000', 10)
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`)
})

res.json() serializes an object to JSON and sets Content-Type: application/json:

app.get('/posts', (req, res) => {
res.json([
{ id: 1, title: 'My first post' },
{ id: 2, title: 'Another post' },
])
})

Status codes tell the client what happened:

RangeMeaningExamples
2xxSuccess200 OK, 201 Created, 204 No Content
4xxClient error400 Bad Request, 401 Unauthorized, 404 Not Found
5xxServer error500 Internal Server Error

Set the status with res.status():

app.post('/posts', (req, res) => {
// ... create post
res.status(201).json({ id: newPost.id, title: newPost.title })
})
app.get('/posts/:id', (req, res) => {
const post = findPost(req.params.id)
if (!post) {
return res.status(404).json({ error: 'Post not found' })
}
res.json(post)
})
MethodUse for
res.json(object)JSON API responses
res.send(text)Plain text or HTML
res.status(code).json(object)Response with explicit status code
res.sendStatus(code)Status code with default message body
res.redirect(url)Redirect to another URL

To read JSON sent in a POST/PUT request body, add express.json() middleware:

app.use(express.json())
app.post('/posts', (req, res) => {
const { title, body } = req.body
console.log(title, body)
res.status(201).json({ message: 'Created' })
})

Without express.json(), req.body is undefined.

Test your API from the terminal without a browser:

Terminal window
# GET request
curl http://localhost:3000/posts
# POST request with JSON body
curl -X POST http://localhost:3000/posts \
-H "Content-Type: application/json" \
-d '{"title": "Hello", "body": "My first post"}'

Or use a GUI tool like Thunder Client (VS Code extension) or Postman.

  1. Add app.use(express.json()) to your server.
  2. Create a POST /posts route that reads title and body from req.body and returns them with a 201 status.
  3. Test with curl or Thunder Client — send {"title": "Hello", "body": "World"}.
  4. What happens if you send a request without Content-Type: application/json? What is req.body?
  • app.listen(PORT, callback) starts the HTTP server.
  • Use process.env.PORT for the port — hosting platforms assign it dynamically.
  • res.json(object) sends a JSON response with the correct Content-Type.
  • res.status(code).json(object) sends a response with an explicit status code.
  • app.use(express.json()) parses JSON request bodies into req.body.