π¦ Serva
The zero setup web framework for Deno. Convention > Configuration, focus on building your app, let Serva do the REST.
Define a route
// ./routes/index.ts
export default () => "Hello, World!";
Start your app
$ serva start
Simple
$ curl localhost:4500
Hello, World!
App directory
Serva requires your app to have a strict file system layout. Currently Serva can only read routes, however in future releases the directory will become very opinionated.
Serva will run on any directory. It will look for a routes
directory to read and
a serva.config.json
for any low-level config (port, hostnames, etc.).
Routes
All routes live inside the routes
directory. They are a mirrored representation
of your appβs endpoints. Lets look at an example directory of the Serva package.
$ tree ./example
example
βββ routes
βΒ Β βββ index.get.ts
βΒ Β βββ index.post.ts
βΒ Β βββ profile
βΒ Β βββ [name].get.ts
βββ serva.config.json
2 directories, 4 files
Without reading a single line of code you can see the available routes your app
will serve. A GET
and a POST
route on the root path, /
. A single route is
defined per file. There can be no fall-through routes. This gives a clear and transparent
overview of any app, no mater who/where/when/how it was developed.
Methods
Routes can be suffixed with a HTTP method to signal that the route should only be
handled by requests that match that method. As you can see from the example directory
they are .get
and .post
. Serva allows the following methods, however can be
configured to accept more, see Method suffix.
Suffix | HTTP Method |
---|---|
.get |
GET |
.post |
POST |
.put |
PUT |
.delete |
DELETE |
.patch |
PATCH |
Any method
If the method suffix is omitted, Serva will bind any/all methods to that route. Serva will first try to match any defined methods before using an any route.
Parameters
Routes can contain parameters within their path. Parameters are a way to bind segments
of the path to the request params
map. Use square brackets around the name of a
parameter within the filename.
$ mkdir ./example/routes/profile
$ touch ./example/routes/profile/[name].get.ts
// ./example/routes/profile/[name].get.ts
import { ServaRequest } from "https://serva.land/serva/mod.ts";
export default ({ params }: ServaRequest) => `Welcome ${params.get("name"}.`;
$ curl localhost:4500/profile/chris
Welcome chris.
Callbacks
Each route must contain a default export as a function. This function is known as the route callback. When Serva matches a route it will invoke this callback with the request.
// ./example/routes/index.get.ts
import { ServaRequest } from "https://serva.land/serva/mod.ts";
export default ({ response }: ServaRequest) => {
response.headers = new Headers({
"X-Powered-By": "Serva",
});
return "Hello from Serva.";
};
Requests
Serva has itβs own request object to preserve the HTTP request object from Denoβs std library. The request object is similar and if you need/require the HTTP version of the object it is also accessible.
interface ServaRequest {
// std http request
readonly httpRequest: http.ServerRequest;
// request
readonly url: URL;
readonly method: string;
readonly params: ReadonlyMap<string, string>;
readonly headers: Headers;
// response
readonly response: http.Response;
}
Responses
Serva exposes a single response object that you can manipulate within your route.
// ./example/routes/index.get.ts
import { ServaRequest } from "https://serva.land/serva/mod.ts";
export default ({ response }: ServaRequest) => {
// set headers
response.headers = new Headers({
"X-Powered-By": "Serva",
});
// set status
response.status = 401;
// set trailers
response.trailers = () =>
new Headers({
"X-Watch-Me": "https://youtu.be/dQw4w9WgXcQ",
});
// return to set the body
return "Hello from Serva.";
};
Config
When Serva reads a serva.config.json
file from the application root it will use
this to configure the app. You can set the following options within the file:
interface ServaConfig {
port: number;
hostname?: string;
extension: string;
methods: string[];
}
Extension
By default Serva will read files with .ts
file extensions. If you prefer to write
your application in JavaScript you can set this to .mjs
or .js
.
Method suffixes
By default Serva will only bind the available methods (see Methods). Set this if you wish to control the method suffixes of your application.
// ./example/serva.config.json
{
methods: [
"get",
"head",
"post",
"put",
"delete",
"options",
"trace",
"patch",
],
}
Running
Serva does not export any application interface. Instead it uses a main import to
instantiate and run an app. To start an application is easy, just tell the app
file to start
with a few permissions.
$ deno run --allow-read --allow-net https://serva.land/serva/app.ts start
Serva requires the following Deno flags to start:
Flag | Reason |
---|---|
--allow-read |
Required for reading the app directory. |
--allow-net |
Required for incoming requests. |
Installation
Alternatively you can install Serva as a Deno binary. If you have configured Deno
correctly you will be able to use the serva
alias to start an application.
$ deno install --name serva --allow-read --allow-net https://serva.land/serva/app.ts
$ serva start
Philosophy
Serva was built to let developers focus on what really matters, the application. Many Node.js frameworks allow developers to setup servers 1001 different ways. Through-out developing and maintaining Node.js apps, each one looks slightly different and you can become lost in how the original developer decided to setup on that day. Let the framework worry about this setup and make Deno apps consistent, letβs not fall into the same pattern.
- Use the filesystem.
- Zero interface.
Roadmap
- β Routes
- ποΈ Hooks (middleware)
- ποΈ Services
- ποΈ Configuration
- ποΈ CLI
- ποΈ Explorer
Key
ποΈ planned
ποΈ development
β
done