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:
$ 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 (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.
$ 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.
$ 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.
$ 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í:
{
"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:
...
"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.
{
"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.
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:
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:
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á!
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
Top comments (0)