Search

intermediate

Building an HTTP Server

Create a web server with routing, request handling, JSON responses, and middleware.

Learning Objectives

  • Set up an HTTP server
  • Define routes and handlers
  • Parse request data
  • Return JSON responses
  • Add middleware

Prerequisites


Project Setup

Create a new project:

Terminal window
mkdir draft-server
cd draft-server
touch main.draft

Routing

Use the http module to create a server and define routes:

import http
action main()
@app = http.create()
app.get("/", home_handler)
app.get("/health", health_handler)
app.post("/tasks", create_task_handler)
write("Server running on http://localhost:8080")
app.listen(8080)
fin action.
action home_handler(req, res)
res.send("Welcome to the Draft API")
fin action.
action health_handler(req, res)
res.json({ status: "ok" })
fin action.

Run the server:

Terminal window
draft run main.draft

Visit http://localhost:8080 in your browser.


Request Handling

Reading Query Parameters

action search_handler(req, res)
@query = req.query
@term = query.get("q") || ""
res.json({
query: term,
results: []
})
fin action.

Reading Request Body

import json
action create_task_handler(req, res)
@body = json.decode(req.body)
@title = body.get("title") || "Untitled"
res.status(201).json({
id: 1,
title: title,
created: true
})
fin action.

JSON Responses

Use res.json to return JSON data:

action tasks_handler(req, res)
@tasks = [
{ id: 1, title: "Learn Draft" },
{ id: 2, title: "Build a server" }
]
res.json(tasks)
fin action.

The response will have Content-Type: application/json set automatically.


Middleware

Middleware runs before your route handler. Use it for logging, auth, and CORS.

Logging Middleware

action logging_middleware(req, res, next)
write(req.method + " " + req.path)
next()
fin action.

CORS Middleware

action cors_middleware(req, res, next)
res.set_header("Access-Control-Allow-Origin", "*")
res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
next()
fin action.

Applying Middleware

action main()
@app = http.create()
app.use(logging_middleware)
app.use(cors_middleware)
app.get("/", home_handler)
app.listen(8080)
fin action.

Complete Server Example

import http
import json
const PORT = 8080
action main()
@app = http.create()
app.use(logging_middleware)
app.use(cors_middleware)
app.get("/api/health", health_handler)
app.get("/api/tasks", list_tasks)
app.get("/api/tasks/:id", get_task)
app.post("/api/tasks", create_task)
app.put("/api/tasks/:id", update_task)
app.delete("/api/tasks/:id", delete_task)
write("Server running on http://localhost:" + PORT)
app.listen(PORT)
fin action.
action logging_middleware(req, res, next)
write(req.method + " " + req.path)
next()
fin action.
action cors_middleware(req, res, next)
res.set_header("Access-Control-Allow-Origin", "*")
res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
next()
fin action.
action health_handler(req, res)
res.json({ status: "ok", uptime: "1h" })
fin action.
action list_tasks(req, res)
@tasks = get_tasks()
res.json(tasks)
fin action.
action get_task(req, res)
@id = int(req.params.get("id"))
@tasks = get_tasks()
@task = tasks.find(t => t.id == id)
if task then
res.json(task)
else
res.status(404).json({ error: "Task not found" })
fi
fin action.
action create_task(req, res)
@body = json.decode(req.body)
@title = body.get("title") || "Untitled"
@tasks = get_tasks()
@new_task = {
id: tasks.length + 1,
title: title,
done: false
}
tasks.push(new_task)
save_tasks(tasks)
res.status(201).json(new_task)
fin action.
action update_task(req, res)
@id = int(req.params.get("id"))
@body = json.decode(req.body)
@tasks = get_tasks()
@task = tasks.find(t => t.id == id)
if not task then
res.status(404).json({ error: "Task not found" })
return
fi
task.title = body.get("title") || task.title
task.done = body.get("done") || task.done
save_tasks(tasks)
res.json(task)
fin action.
action delete_task(req, res)
@id = int(req.params.get("id"))
@tasks = get_tasks()
@index = -1
loop i from 0 to tasks.length - 1
if tasks[i].id == id then
index = i
fi
again
if index >= 0 then
tasks.remove(index)
save_tasks(tasks)
res.status(204).send("")
else
res.status(404).json({ error: "Task not found" })
fi
fin action.
action get_tasks()
if not fs.exists("tasks.json") then
return []
fi
try
@content = fs.read("tasks.json")
return json.decode(content)
catch error
return []
end try
fin action.
action save_tasks(tasks)
fs.write("tasks.json", json.encode(tasks))
fin action.

Try It Yourself

  1. Save the code as main.draft.
  2. Run draft run main.draft.
  3. Test with curl:
Terminal window
curl http://localhost:8080/api/health
curl http://localhost:8080/api/tasks
curl -X POST http://localhost:8080/api/tasks -H "Content-Type: application/json" -d '{"title":"Learn Draft"}'

Checkpoint

  1. Which function starts the HTTP server?

    • app.start(port)
    • app.listen(port)
    • http.serve(port)
  2. How do you set a response header?

    • res.header("key", "value")
    • res.set_header("key", "value")
    • res.headers["key"] = "value"
  3. What does next() do in middleware?

    • Stops the request
    • Passes control to the next middleware or handler
    • Sends a response
Answers
  1. app.listen(port)
  2. res.set_header("key", "value")
  3. Passes control to the next middleware or handler

Summary

You built a REST API server with routing, JSON handling, and middleware. The same patterns scale to larger applications.

Next Steps