Aqua
Aqua is a minimal and fast web framework.
Features
- Immediate parsing of the request body, query and the cookie string
- Middleware functions
- Possibility for route changes while runtime
- URL parameters
- Blazing fast
Example usage
import Aqua from "https://deno.land/x/aqua/mod.ts";
const app = new Aqua(3100);
app.get("/", (req) => {
return "Hello, World!";
});
Routing
You can either use the short-form syntax for the GET
and POST
method
app.get("/", (req) => "Hello, World!");
app.post("/", (req) => "Hello, World!");
or use the route function
app.route("/", "GET", (req) => "Hello, World!");
Schemas
Schemas will discard non-matching requests (Defined fallback handler or default 404).
app.get("/", (req) => {
return "Hello, World!";
}, {
schema: {
query: [
mustExist("hello")
]
}
});
This schema would only allow requests with the hello
query (for example, GET /?hello=yes
).
The following helper functions are currently available:
mustExist(key)
valueMustBeOfType(key, type)
mustContainValue(key, values)
You can of course also build your own schema validation functions.
Hereβs how the mustExist
function looks:
function mustExist(key: string): RoutingSchemaValidationFunction {
return function() {
/*
`this` depends on the context, it could either be the
query, parameters, cookies or body object.
Luckily, they all have the same type.
*/
return Object.keys(this).includes(key);
};
}
Middlewares
You can register middlewares, that will be able to adjust the response object, the following way:
app.register((req, res) => {
/*
const textContent = res.content instanceof Uint8Array
? new TextDecoder().decode(res.content)
: res.content;
res.content = textContent?.replace("Hello", "Hi");
*/
return res;
});
URL parameters
You can define URL parameters by using a colon followed by the key name.
app.get("/api/:action", (req) => {
return req.parameters.action;
});
Response value
You can either just return a string
app.get("/", (req) => {
return "Hello, World!";
});
or return a response object to also set cookies, headers or a status code
app.get("/", (req) => {
return {
statusCode: 200,
cookies: { hello: "I'm a cookie value" },
headers: { hello: "I'm a header value" },
content: "Hello, World!"
};
});
Cookies and headers are just getting appended, so no information is getting lost by providing custom ones. However, you can still overwrite existing headers.
Benchmarks
Framework | Version | Avg RPS | Router? |
---|---|---|---|
Deno HTTP | 0.75.0 | 19138 | No |
Aqua | 1.0.8 | 17726 | Yes |
Drash | 1.2.5 | 16022 | Yes |
Fen | 0.8.0 | 7071 | Yes |
Abc | 1.2.0 | 4676 | Yes |
Last updated: 2020-11-01
More examples
Respond with the content of a file
app.get("/", async (req) => {
return await app.render("index.html");
});
Please note that you must run your application with the --allow-read
flag.
Provide own fallback handler
Your provided fallback handler will be executed if no route has been found.
app.provideFallback((req) => {
return "No page found, sorry!";
});
Redirect a request
app.get("/dashboard", (req) => {
return { redirect: "/login" };
});
Static routes
You can register static routes by passing the path to the local folder and
the public alias to the serve
function.
app.serve("mystaticfolder", "/public");
// A GET request to /public/test.txt would serve the local file at mystaticfolder/test.txt
Regex paths
You can provide a RegExp object instead of a string and receive the matches.
app.get(new RegExp("\/(.*)"), (req) => {
console.log(req.matches); // GET /hello-world -> [ "hello-world" ]
return "Hello, World!";
});
TLS
You can enable TLS the following way:
const app = new Aqua(3001, {
tls: {
hostname: "localhost",
certFile: "localhost.crt",
keyFile: "localhost.key"
}
});
The example above would handle requests coming to https://localhost:3001
.
Handle HTTP and HTTPS requests
You are able to provide the TLS certificate to a different port and let the default port still handle HTTP requests.
const app = new Aqua(3001, {
tls: {
hostname: "localhost",
certFile: "localhost.crt",
keyFile: "localhost.key",
independentPort: 3002
}
});
The example above would allow you to handle requests to http://localhost:3001
and https://localhost:3002
at the same time.
File uploading
app.post("/upload", async (req) => {
const { newProfilePicture } = req.files;
await Deno.writeFile(newProfilePicture.name, new Uint8Array(await newProfilePicture.arrayBuffer()));
return "Uploaded!";
});