http-nu 
http-nu lets you attach a Nushell closure to an HTTP
interface. If you prefer POSIX to Nushell, this
project has a cousin called http-sh.
Install
Overview
GET: Hello world
Reading closures from stdin
You can also pass - as the closure argument to read the closure from stdin:
|
This is especially useful for more complex closures stored in files:
|
Check out the examples/basic.nu file in the repository for a complete example that implements a mini web server with multiple routes, form handling, and streaming responses.
You can listen to UNIX domain sockets as well
TLS Support
Enable TLS by providing a PEM file containing both certificate and private key:
Generate a self-signed certificate for testing:
Serving Static Files
You can serve static files from a directory using the .static command. This
command takes two arguments: the root directory path and the request path.
When you call .static, it sets the response to serve the specified file, and
any subsequent output in the closure will be ignored. The content type is
automatically inferred based on the file extension (e.g., text/css for .css
files).
Here's an example:
POST: echo
Request metadata
The Request metadata is passed as an argument to the closure.
&abc=123
Response metadata
You can set the Response metadata using the .response custom command.
.response {
status: <number> # Optional, HTTP status code (default: 200)
headers: { # Optional, HTTP headers
<key>: <value>
}
}
$ http-nu :3001 '{|req| .response {status: 404}; "sorry, eh"}'
$ curl -si localhost:3001
HTTP/1.1 404 Not Found
transfer-encoding: chunked
date: Fri, 31 Jan 2025 08:20:28 GMT
sorry, eh
Content-Type Inference
Content-type is determined in the following order of precedence:
-
Headers set via
.responsecommand:.response { headers: { "Content-Type": "text/plain" } } -
Pipeline metadata content-type (e.g., from
to yaml) -
For Record values with no content-type, defaults to
application/json -
Otherwise defaults to
text/html; charset=utf-8
Examples:
# 1. Explicit header takes precedence
{|req| .response {headers: {"Content-Type": "text/plain"}}; {foo: "bar"} } # Returns as text/plain
# 2. Pipeline metadata
{|req| ls | to yaml } # Returns as application/x-yaml
# 3. Record auto-converts to JSON
{|req| {foo: "bar"} } # Returns as application/json
# 4. Default
{|req| "Hello" } # Returns as text/html; charset=utf-8
Streaming responses
Values returned by streaming pipelines (like generate) are sent to the client
immediately as HTTP chunks. This allows real-time data transmission without
waiting for the entire response to be ready.
)
)
)
)
)
server-sent events
TODO: we should provide a to sse built-in
# simulate generating events in a seperate process