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.
How HTTP works
Section titled “How HTTP works”When a client (browser, React app, curl) makes a request:
- It opens a TCP connection to your server’s IP address and port
- Sends an HTTP request: method + path + headers + optional body
- Your server handles the request and sends an HTTP response: status code + headers + body
- 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— HTTPS3000,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}`)})Sending JSON responses
Section titled “Sending JSON responses”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' }, ])})HTTP status codes
Section titled “HTTP status codes”Status codes tell the client what happened:
| Range | Meaning | Examples |
|---|---|---|
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 4xx | Client error | 400 Bad Request, 401 Unauthorized, 404 Not Found |
| 5xx | Server error | 500 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)})Response methods
Section titled “Response methods”| Method | Use 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 |
Reading the request body
Section titled “Reading the request body”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.
Testing with curl
Section titled “Testing with curl”Test your API from the terminal without a browser:
# GET requestcurl http://localhost:3000/posts
# POST request with JSON bodycurl -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.
Exercise
Section titled “Exercise”- Add
app.use(express.json())to your server. - Create a
POST /postsroute that readstitleandbodyfromreq.bodyand returns them with a 201 status. - Test with curl or Thunder Client — send
{"title": "Hello", "body": "World"}. - What happens if you send a request without
Content-Type: application/json? What isreq.body?
app.listen(PORT, callback)starts the HTTP server.- Use
process.env.PORTfor 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 intoreq.body.