Networking
Now we’re getting to the really fun stuff! The net
library lets your scripts talk to the internet - whether that’s fetching data from websites, calling APIs, or even creating your own web servers.
Making Web Requests
Section titled “Making Web Requests”Let’s start with something simple - fetching a web page:
local net = require("@lune/net")
local response = net.request("https://www.example.com")
if response.ok then print(`Success! Got {#response.body} bytes`) print(`Status: {response.statusCode} {response.statusMessage}`)else print(`Request failed: {response.statusCode}`)end
When you make a request, you get back a response object with everything you need - the status code, headers, body content, and an ok
field that tells you if things went well.
Working with APIs
Section titled “Working with APIs”Most modern web services use APIs that speak JSON. Here’s how you can work with them:
local net = require("@lune/net")local serde = require("@lune/serde")
-- Let's search GitHub for popular Luau projectslocal response = net.request({ url = "https://api.github.com/search/repositories", query = { q = "language:luau", sort = "stars", per_page = "3" }})
if response.ok then local results = serde.decode("json", response.body) print(`Found {results.total_count} Luau repositories!\n`)
for _, repo in results.items do print(`⭐ {repo.stargazers_count} - {repo.full_name}`) print(` {repo.description}\n`) endend
When you need to send data to an API, you’ll typically use methods other than GET, such as POST or PATCH:
local response = net.request({ url = "https://api.example.com/data", method = "POST", headers = { ["Content-Type"] = "application/json" }, body = serde.encode("json", { name = "My Lune Script", version = "1.0.0" })})
The serde
library handles all the JSON encoding and decoding for you, so you can work with regular Lua tables and other datatypes.
Running a Web Server
Section titled “Running a Web Server”Sometimes you don’t want to make requests - you want to receive them. Creating a web server with Lune is surprisingly easy:
local net = require("@lune/net")
local visitCount = 0
net.serve(8080, function(request) visitCount += 1
print(`[{request.method}] {request.path} - Visit #{visitCount}`)
if request.path == "/" then return { status = 200, headers = { ["Content-Type"] = "text/html" }, body = `<h1>Hello, visitor #{visitCount}!</h1> <p>Try visiting <a href="/api">/api</a></p>` } elseif request.path == "/api" then return { status = 200, headers = { ["Content-Type"] = "text/plain" }, body = `You are visitor number {visitCount}` } else return { status = 404, body = "Page not found" } endend)
print("Server running at http://localhost:8080")print("Press Ctrl+C to stop")
Your server can handle different routes, check request methods and headers, parse request bodies - everything you need for building real web applications.
Beyond HTTP
Section titled “Beyond HTTP”The net
library has even more tricks up its sleeve. It can handle WebSockets for real-time communication, raw TCP connections for custom protocols, and more.
WebSockets
Section titled “WebSockets”WebSockets are perfect when you need real-time, two-way communication. Lune makes these very easy to use as well:
local net = require("@lune/net")
-- Connect to a WebSocket echo serverlocal socket = net.socket("wss://echo.websocket.org")
socket:send("Hello from Lune!")
-- Wait for the echolocal reply = socket:next()print(`Server echoed: {reply}`)
socket:close()
Extra: WebSocket Echo Server
Here’s a fun example that combines HTTP and WebSocket handling to create an interactive echo server:
local net = require("@lune/net")
net.serve(8080, { handleRequest = function(request) return { status = 200, headers = { ["Content-Type"] = "text/html" }, body = [[ <h1>WebSocket Echo Test</h1> <input id="message" placeholder="Type a message"> <button onclick="send()">Send</button> <div id="log"></div> <script> const ws = new WebSocket('ws://localhost:8080'); const log = document.getElementById('log');
ws.onmessage = (e) => { log.innerHTML += `<p>Echo: ${e.data}</p>`; };
function send() { const input = document.getElementById('message'); ws.send(input.value); input.value = ''; } </script> ]] } end, handleWebSocket = function(socket) print("WebSocket connected!")
for message in socket do print(`Received: {message}`) socket:send(`Echo: {message}`) end
print("WebSocket disconnected") end})
print("Echo server running at http://localhost:8080")
Try running this and opening http://localhost:8080 in your browser. You’ll have a working chat interface!
Advanced: TCP Connections
For those times when you need to speak a protocol that isn’t HTTP, you can use raw TCP connections, optionally backed by TLS:
local net = require("@lune/net")
-- Connect to example.com on port 80 (HTTP)local conn = net.tcp.connect("example.com", 80)
-- Send a raw HTTP requestconn:write("GET / HTTP/1.1\r\n")conn:write("Host: example.com\r\n")conn:write("Connection: close\r\n")conn:write("\r\n")
-- Read the responselocal response = conn:read()print("Response received:")print(response)
-- Keep reading until the server closes the connectionwhile true do local chunk = conn:read() if chunk == nil or #chunk == 0 then break end print(chunk)end
conn:close()
Here’s how you can use TLS for secure connections - add a simple flag argument for enabling it:
-- Connect with TLS enabledlocal secure = net.tcp.connect("example.com", 443, true)
This gives you the power to implement any TCP-based protocol - SMTP, FTP, custom game protocols, you name it. Lune does not currently provide built-in support for databases, but with TCP connections you can use existing clients built for other runtimes without much extra effort.
What’s Next?
Section titled “What’s Next?”With all this network power at your fingertips, you’ll probably want to save some of that data you’re fetching. Let’s explore how to work with files and directories in Working with Files.