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
- Completed Building a CLI Tool
- Understanding of HTTP basics
Project Setup
Create a new project:
mkdir draft-servercd draft-servertouch main.draftRouting
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:
draft run main.draftVisit 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 httpimport 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" }) fifin 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" }) fifin 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 tryfin action.
action save_tasks(tasks) fs.write("tasks.json", json.encode(tasks))fin action.Try It Yourself
- Save the code as
main.draft. - Run
draft run main.draft. - Test with curl:
curl http://localhost:8080/api/healthcurl http://localhost:8080/api/taskscurl -X POST http://localhost:8080/api/tasks -H "Content-Type: application/json" -d '{"title":"Learn Draft"}'Checkpoint
-
Which function starts the HTTP server?
app.start(port)app.listen(port)http.serve(port)
-
How do you set a response header?
res.header("key", "value")res.set_header("key", "value")res.headers["key"] = "value"
-
What does
next()do in middleware?- Stops the request
- Passes control to the next middleware or handler
- Sends a response
Answers
app.listen(port)res.set_header("key", "value")- 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
- Using WebAssembly — Compile Draft to WASM and run it in the browser.
