Introduction to Tino - tiny and functional HTTP server for Deno
Tags: deno, javascript, functional, tino
Published: 2020-08-07 08:39:26

## Little bit of theory

Since [Deno](https://deno.land/) released its version 1.0 earlier this year I was eager to make an HTTP server for it. And, few weeks later I had some basic routing... thing, I called [Tino](https://github.com/Vertrical/tino). It's tiny, it's for Deno.

Tino also offers local JSON REST API for rapid prototyping via responders. More on that when you scroll down.

Keep in mind, it is only JavaScript for now, so no TypeScript, but, that doesn't matter really since we can run both in Deno. We're waiting for [this issue](https://github.com/microsoft/TypeScript/issues/38510) to become ready and TS 4.0 version bundled into Deno to make Tino fully TS, too. Why? Because [variadic tuples](https://github.com/microsoft/TypeScript/issues/5453) are not yet supported and Tino uses function composition heavily.

I didn't want to have it "java-esque" behaviour with decorated routes and classes and what not (think of Spring Boot for example) only because it is now possible to write TypeScript out of the box. Not saying it's a bad thing, on the contrary.

On the other hand Tino uses only functions and (async) function composition. Async composition (for middlewares) is necessary so you can compose both sync and async functions.

## Usage and examples

Let's see how it actually works and how much freedom one might have using functions only. (Take a look at [examples](https://github.com/Vertrical/tino/blob/develop/examples.js) any time) 

### First steps

Import Tino from third party modules (for version number look at [README.md](https://github.com/Vertrical/tino/blob/develop/README.md)):
```js
import tino from "https://deno.land/x/tino@vX.Y.Z/tino.js";
const app = tino.create();
// ... you business logic
tino.listen({ app, port: 8000 });
console.log(`Server running at 8000`);
```
Now we can focus on the rest of your logic, i.e. defining endpoints, controllers and responders.

Defining your first endpoint is as easy as:
```js
const use = () => ({ resp: "pong" });
app.get(() => ({ path: "/ping", use }));

// Even shorter, but only for prototyping:
app.get(() => ({ path: "/ping", resp: "pong" }));
```
`use` is your controller. It is flexible and used also for extending Tino's functionality and custom attributes. Read on for more on that.

Both `use` and `resp` can be functions, but it makes more sense for `use` - if `resp` is a function, it can still receive props but will be called internally and its return will be used as return for `use`. ;)

Let's see what controller(`use`) can be and what it can return:
```js
// A function or async function, only returning a string (can be any primitive)
// content-type: text/plain
const returnPong = ({ resp: "pong" })
const use1 = () => returnPong;
const use2 = async () => returnPong;
app.get(() => ({ path: "/ping", use: use1 }));

// Return an object:
// content-type: application/json
const use = () => ({ resp: () => ({}) });

// Set type and status
const use = () => ({ resp: () => "pong", status: 201, type: "text/plain" });
```
### Named params
Tino uses named params for defining your routes, like:
```js
app.get(() => ({ path: "/users/:id", use }));
```

### Props
Any controller can receive props:
```js
const use = (props) => ({ resp: props.params.id });
app.post(() => ({ path: "/users/:id", use }));
```

#### Prop type
Prop is an object with following attributes:

1. body - body sent from POST, PUT and PATCH methods
2. params - parameters from named params
3. query - query object from string like ?p=1&q=2
4. custom params
5. matchedPath - path regex
6. pathPattern - path definition
7. req - as `{ method, url }`
8. Any other parameters coming from middlewares

### Middlewares

Middlewares are provided through async composition of functions. Each function in chain must return properties which are required for next function in chain, or which are required to pass to controller at the end of the chain.

Let's say we have two async functions and one sync, chained together:
```js
// first we need `useMiddlewares` helper for composition
import { withMiddlewares } from "./tino.js";
const withAuth = async (props) => ({ currentUser: {}, userData: props.body });
const isAdmin = ({ currentUser }) => ({ isAdmin: false, currentUser });
const withDB = async (props) => ({ coll: {}, ...props });

// Then we chain(compose) them:
const composed = useMiddlewares(
  withAuth,
  isAdmin,
  withDB,
);

// Then we wrap our controller with it:
const use = composed((props) => ({ resp: props.currentUser }));
app.get(() => ({ path: "/myapi", use }));
```

#### Throw exceptions in middlewares (protect routes)
If you want to return early from middleware chain, just throw an exception with same definition as controller's `{ resp, status?, type? }`:
```js
const withAuth = async () => { throw { resp: "Boom", status: 401 } };
```
So whatever you return from your controller, your endpoint result will be:
```
HTTP/1.1 401 Unauthorized
content-length: 4
content-type: text/plain

Boom
```

## Responders

Responders are different set of functions which are useful for writing your own namespaced endpoints or let other people use your package with Tino.

To define one just add `root: true` param to endpoint definition:
```js
app.any(() => ({ path: "/api/v2", use: myAPI.v2, root: true }));
```
`.any` stands for any of the HTTP methods so your namespace reacts to all of them.

Your `myAPI.v2` function will receive `ctx` object which contains some Deno stuff like:
```js
{
  req: ServerRequest,
  body,
  query,
  params,
  use, // reference to your function
}
```

### `jsondb` responder

This responder comes built-in with Tino. It opens `/api` path by default and is responsible for restful CRUD operations against local `db.json` file. To learn more about it check it out: [https://github.com/Vertrical/tino/blob/develop/README.md#using-jsondb-responder](https://github.com/Vertrical/tino/blob/develop/README.md#using-jsondb-responder).

Thank you for reading this far about tino and I hope you liked it. 

Again, to see how tino can be used check out maintained [examples](https://github.com/Vertrical/tino/blob/develop/examples.js). Tino is under heavy development and expect more to come and more articles. ;)

Cheers! 🍻