Nio is a small, statically typed language. It compiles your code to one native program, finds mistakes before it runs, and comes with an HTTP server, TLS and cryptography in the box.
import 'http';
import 'json';
type User {
String? id;
String name;
}
http.Response showUser(http.Request req) {
User u = { id: req.params["id"], name: "Ada" };
return http.json(200, json.toText(u));
}
http.Server app = http.server();
app.route(http.Method.GET, "/", http.Response (http.Request r) -> http.text(200, "hello"));
app.route(http.Method.GET, "/users/:id", http.Response (http.Request r) -> showUser(r));
await app.listen("0.0.0.0", 8080, null) catch e {
print(`cannot listen: ${e.message}`);
};A complete web service. nio build --release server.nio turns it into a single file you can copy to any server.
The things Nio is built to do well.
Nio compiles through LLVM to one native executable. There is no virtual machine and nothing to install where it runs. Release builds optimize the whole program, runtime included.
Every value is checked before the program runs. Numbers never change type on their own, and a String is never null: a value that can be missing is a String?, and you must check it first.
The compiler works out which functions can fail. Errors go up to the caller by themselves, and you handle them with catch where it makes sense. No exceptions and no hidden jumps.
async and await let one program serve many connections at once, while it waits for the network, the disk or a timer. There are no threads, so there are no locks and no data races.
An HTTP server and client, TLS 1.3, cryptography, JSON, sockets, files, processes and regular expressions are all in the standard library. You need no packages to start.
Each value uses only its own width, so a byte[] takes one byte per element. The garbage collector gives memory back to the system when the program goes quiet.
TLS checks certificates unless you say not to. Regular expressions run in linear time. Packages are checked against a hash on every build and never run code when you install them.
A formatter, a test runner, a documentation viewer, debugger support and a language server for your editor all come in the one nio command. You only need clang.
The same small programs written in Nio, C, Go, Java and Node.js, line for line, each checked to print the same result before it is timed.
Higher is better. Requests per second: how many complete requests the server answered each second, so 139k means 139,000 responses every second. Averaged over three small routes, over keep-alive connections on the same machine, and the median of three passes. Memory, underlined, is the peak memory of the server process.
1 connectionOne client sends a request only after it gets the previous response, 200,000 times per route.This measures how long one request takes to go through each HTTP stack.Each server has three routes: /plaintext answers "Hello, World!", /json answers it as a JSON object, and /users/:id answers with a path parameter.One load generator, written in Go, sends keep-alive requests over loopback without pipelining. Opening connections is not timed.Java is the JDK's built-in server with one virtual thread per request, and no libraries from outside the JDK.one client, one request at a time100 connections100 clients send requests at the same time, 200,000 requests per route.Nio and Node.js serve from one thread, and Go and Java are limited to one core to match. Go (multicore) and Java (multicore) use all 10 cores.Each server has three routes: /plaintext answers "Hello, World!", /json answers it as a JSON object, and /users/:id answers with a path parameter.One load generator, written in Go, sends keep-alive requests over loopback without pipelining. Opening connections is not timed.Java is the JDK's built-in server with one virtual thread per request, and no libraries from outside the JDK.many clients at once1,000 connections1,000 clients send requests at the same time, 1,000,000 requests per route.This shows how each server handles many open connections, and how much memory they cost.Each server has three routes: /plaintext answers "Hello, World!", /json answers it as a JSON object, and /users/:id answers with a path parameter.One load generator, written in Go, sends keep-alive requests over loopback without pipelining. Opening connections is not timed.Java is the JDK's built-in server with one virtual thread per request, and no libraries from outside the JDK.a thousand clients at onceMeasured on Apple M4 (10 cores), macOS, clang 21, Go 1.26, Java 23, Node.js 24, with the scripts in the Nio repository's benchmark folder. Nio and C are compiled with -O2. These are small programs that each stress one thing, so a real application will differ. In the HTTP test the load generator runs on the same machine as the server. Nio and Node.js serve from one thread, so Go and Java are limited to one core to match. Go (multicore) and Java (multicore) show what they do with all 10.
Records turn into JSON and back with one call. A field that can be missing says so in its type, and the compiler does not let you use it before you check it. A function that can fail needs no special signature: its errors go up to the caller, and catch handles them where you choose.
import 'fs';
import 'json';
import 'string';
type Config {
String host;
int port;
String? name; // may be missing: the compiler makes you check
}
Config load(String file) {
byte[] raw = fs.readFile(file); // can fail: the error goes up
return json.parse(string.fromByteArray(raw)) as Config;
}
Config? cfg = load("config.json") catch null;
if (cfg != null) {
print(`${cfg.host}:${cfg.port}`); // localhost:8080
}The compiler is written in Nio, and clang is the only thing it needs. It runs on macOS, Linux and Windows.
nio run app.nioCompile and run in one step while you work.
nio build --release app.nio -o appBuild an optimized native program.
./appCopy the one file to a server and run it.