Configurar Api REST con koa y typescript
Tags: node, typescript, koa
Published: 2020-09-03 02:33:44

> Nodejs 12.18.2 - Typescript 4.02 - Koa 2.13 - ts-node 9.0.0 - tsc-watch 4.2.9

Aunque he trabajado _node_ con _javascript_ hace ya varios años, no había tenido la oportunidad de hacerlo con _typescript_. En el trabajo, acabo de iniciar un nuevo proyecto donde se requiere la creación de varios microservicios con _typescript_ y ví ésta como la oportunidad perfecta para mostrarles (y aprender claro!) todo lo relacionado a la configuración y puesta en marcha de un api rest. Así que, comencemos:

# Configuración

Primero, crearemos nuestro proyecto, para ello ejecutamos en la consola el comando: 

```bash
$ npm init -y
```

Esto crea el archivo **package.json** que contendrá los módulos usados en este proyecto junto con otras configuraciones que adicionaremos.

Vamos a construir el api rest con [Koa](https://koajs.com/) (de los mismos creadores de express); un módulo similar a express pero mucho más liviano, rápido y sólido que nos permite ir añadiendo características a medida que vamos construyendo.

```bash
$ npm i koa koa-basic-auth koa-bodyparser koa-logger koa2-cors koa-mount koa-router
```

* koa-basic-auth: Manejar autenticación básica http. 
* koa-bodyparser*: Interpreta y parsea el body para un tratamiento más fácil.
* koa-logger*: Loggea las peticiones http en consola que van llegando. 
* koa2-cors*: Para el soporte de cors
* koa-mount*: Para montar un middleware sobre la aplicación.
* koa-router*: Para manjear las rutas del api.


Ahora instalamos las dependencias de desarrollo entre ellas nodemon que ya conocemos.

```bash
$ npm i -D typescript nodemon  ts-node tsc-watch @types/koa @types/koa-bodyparser @types/koa-logger @types/koa-mount @types/koa-router @types/koa2-cors @types/koa-basic-auth
```

El módulo de `tsc-watch` nos permite ejecutar un comando después de que haya compilado el código y los `@types` son los complementos de los módulos que vamos a usar pero para typescript.

En desarrollo, me gusta poder trabajar directamente con typescript, es decir, cuando ejecutemos el código, le indico a node a través del módulo `ts-node` que ejecute directamente el archivo de entrada del api `server.ts` en vez del  código transpilado `dist/server.js` para facilitar algunos procesos como el debug.

Super importante, ahora, con la ayuda del `tsc` (typescript compiler), creamos el archivo que le dirá a typescript cómo compilar el código y qué reglas deberá seguir.

```bash
$ npx tsc --init
```

O solo `tsc --init` si ya tenemos instalado typescript globalmente. Una vez ejecutado el código, nos crea un archivo `tsconfig.json` que modificaremos para:

* Transpilar el código a ES6
* Ser usado en un entorno de nodejs
* Indicar el directorio de salida (`/dist`) de los archivos js y el directorio raiz donde se encuentra todo el código ts.
* Indicarle en qué directorio(s) se encuentra el código ts y cuáles debe excluir y no analizar durante el proceso de transpilación.

Adicional a las opciones con las que viene el archivo, agregaremos unas propiedades para excluir e incluir algunos archivos en la compilación de tal forma que el `tsconfig.json` se vea así:

```json
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    
    "target": "es6",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
     "outDir": "./dist",                        /* Redirect output structure to the directory. */
     "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    
    /* Module Resolution Options */
    "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    
    /* Experimental Options */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */

    /* Advanced Options */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  },
  "include": ["./**/*"],
  "exclude": ["node_modules", "./**/*.spec.ts"]
}

```

Antes de escribir la primera línea de código, vamos a terminar de configurar el punto de entrada de nuestra api junto con algunos comandos útiles en nuestro **package.json** reemplazando la sección de `scripts` con el siguiente código:

```json
...
"scripts": {
    "dev": "nodemon",
    "dev:js": "tsc-watch --onSuccess \"node dist/server.js\" ",
    "build": "tsc dist/settings",
    "start": "node dist/server.js"
  }
...
```

Finalmente, creamos el archivo **nodemon.json** para que el server en desarrollo se ejecute después de cada cambio que hagamos de forma automática y no tener que estar todo el tiempo levantando y bajando el servidor.

```json
{
  "watch": ["./"],
  "ext": "ts",
  "ignore": ["./**/*.spec.ts"],
  "exec": "ts-node ./server.ts"
}
```

# Api rest

Empecemos creando el archivo `app.ts` que será el archivo principal de la aplicación. Allí, inicializamos nuestra aplicación con _koa_ configuramos nuestros middlewares de auteticación básica, cors, logger y el bodyparser, finalmente adicionamos el router que deseamos en el api. 

```typescript
import koa from "koa";
import bodyparser from "koa-bodyparser";
import loggerKoa from "koa-logger";
import cors from "koa2-cors";
import mount from "koa-mount";
import auth from "koa-basic-auth";
import health from "./health";

//init
const app = new koa();

//middlewares
app.use(cors());
app.use(loggerKoa());
app.use(bodyparser());
app.use(mount("/health", auth({
    name: 'user',
    pass: 'password',
})));

//Routes
app.use(health.routes());

//export server
export default app;

```

Creamos ahora el router que manejará las rutas y el response para cada caso. Para ello creamos el archivo `health.ts` y ponemos el siguiente código:

```typescript
import Router, { IRouterParamContext } from "koa-router";

//init
const healthRouter = new Router();

healthRouter.get("/", async (ctx) => {
  ctx.status = 200;
  ctx.body = "ok";
});

healthRouter.get("/health", async (ctx) => {
  
  ctx.status = 200;
  ctx.body =  {
    nodeVersion: process.version,
    service: 'TypeScriptNode',
    memory: process.memoryUsage(),
    pid: process.pid,
    uptime: process.uptime(),
    environment: 'dev',
    appVersionPackage: "1.0.0",
    };
});


export default healthRouter;
```

Por último creamos el servidor http que llama nuestra aplicación:

```typescript
import { Server } from "http";
import { AddressInfo } from "net";
import app from "./app";

class ApiServer {
  server!: Server;
  public constructor() {}

  listen = () => {
    const PORT = process.env.PORT || 3000;
    this.server = app.listen(PORT, async () => {
      console.log(
        `When it's ${new Date().toLocaleString()} we are getting ready`
      );
      console.log(`Starting in ${process.env.NODE_ENV} mode`);
      console.log(`Listening on ${PORT}`);
    });
  };

  close = () => {
    this.server.close();
  };

  address = () => {
    return this.server.address() as AddressInfo;
  };
}

const server = new ApiServer();
server.listen();
```

Y ejecutamos el api con ayuda de nodemon con el comando `$ npm run dev` y con postman hacemmos la petición `GET` al endpoint configurado y voalá!

![TypeScriptNode](https://dev-to-uploads.s3.amazonaws.com/i/rydnajn2usalfn749ady.png)


Listo! tenemos entonces configurado nuestro proyecto base de typescript con node, sencillo, práctico y muy útil al iniciar una nueva api. 

Nos vemos.

> source code https://gitlab.com/makitocode/typescript-node-api