npm package: @mersocarlin/node-server
Description: Enhanced fastify server cookie-parser, cors, helmet, rate-limiter and opt-in Mongo DB connection.
Author: Hemerson Carlin
Latest version: 1.1.0

# @mersocarlin/node-server

[![npm version](https://badge.fury.io/js/%40mersocarlin%2Fnode-server.svg)](https://badge.fury.io/js/%40mersocarlin%2Fnode-server)

Enhanced fastify server with `cookie-parser`, `cors`, `helmet`, rate-limiter and opt-in Mongo DB connection.

## Motivation

Whenever I write a new node server, I end up creating the same boilerplate using similar express middleware: `cookie-parser`, `helmet`, `cors`, `express-rate-limit`... not to mention the [default error handler](https://expressjs.com/en/guide/error-handling.html) for express apps.

Just thought about extracting all that into a single package so I can focus only on the api routes + business logic of my node-server and leave common config to be handled somewhere else.

## Install

```bash
yarn add @mersocarlin/node-server
```

```bash
npm i @mersocarlin/node-server
```

## What's included

- `fastify-cookie`
- `fastify-cors`
- `fastify-helmet`
- `fastify-rate-limit`
- `mongoose` connection (_opt-in_)
- basic repository for CRUD entities

## Usage

```ts
import { FastifyInstance } from 'fastify'
import { createServer } from '@mersocarlin/node-server'

/**
 * Setup custom routes and/or plugins
 **/

function onRegisterPlugins(fastifyInstance: FastifyInstance) {
  fastifyInstance.post('/signin', async () => {
    return {
      loggedIn: true,
    }
  })

  fastifyInstance.get('/users/:userId', async (request) => {
    return {
      id: request.params.userId,
    }
  })
}

import { createServer } from '@mersocarlin/node-server'

const fastifyServer = createServer({
  appName: 'my-custom-server',
  onRegisterPlugins,
  version: '1.0.0',
})

async function startServer() {
  try {
    await testFastifyServer.listen(PORT, '0.0.0.0')
  } catch (err) {
    testFastifyServer.log.error(err)
    process.exit(1)
  }
}
```

**Note**: If you have a single node-server you might not need this package at all.
In my case though, I have a few APIs spread across different repos and I realised they all share the same initial config.

### Mongo repository

#### Create Entity types

```ts
import { Document } from 'mongoose'

interface IEntity {
  name: string
  value: string
}

// Used for querying DB
interface IEntityQuery {
  name?: string
  value?: string
}

interface IMyDocument extends Document {
  name: string
  value: string
}
```

#### Create schema and model

```ts
import mongoose, { Schema } from 'mongoose'

const MySchema = new Schema({
  name: { type: String, required: true },
  value: { type: String, required: true },
})

const myModel = mongoose.model<IMyDocument>('mymodel', MySchema)
```

#### Create repository and start CRUD

```ts
import { createRepository } from '@mersocarlin/node-server'

const modelRepository = createRepository<IMyDocument, IEntityQuery, IEntity>(
  myModel
)

// Save to DB
const dbEntity = await modelRepository.create({
  name: 'Test',
  value: 'Some value',
})

// Update
await modelRepository.update(dbEntity._id, {
  name: 'New Name',
  value: dbEntity.value,
})

// List
const allEntities = await modelRepository.find({})
const byName = await modelRepository.find({ name: 'New name' })

// Delete
await modelRepository.remove(dbEntity._id)
```

### Response Time

#### Built-in

```ts
import { createServer } from '@mersocarlin/node-server'
import appRoutes from 'path/to/appRoutes.js'

const app = createServer({
  appName: 'my-custom-server',
  onResponseTime: ({ duration, requestName }) => {
    console.log(`${requestName} took ${duration}ms`)
  },
  routesHandler: appRoutes,
  version: '1.0.0',
})
```

## API

### `createServer`

| Prop              | Type     | Default                                  | Description                                                                        |
| ----------------- | -------- | ---------------------------------------- | ---------------------------------------------------------------------------------- |
| appName           | String   |                                          | Your server or app name. Ex: `my-rest-service`                                     |
| healthcheckPath   | String   | `/healthcheck`                           | Defines healthcheck endpont for node server                                        |
| mongoDBConfig     | Object   |                                          | Defines Mongo DB configuration for database connection                             |
| onResponseTime    | Function |                                          | Custom callback to track response time of each request                             |
| onRegisterPlugins | Function |                                          | Custom callback setup your custom routes and plugins                               |
| rateLimitConfig   | Object   | `{ windowMs: 15 * 60 * 1000, max: 100 }` | Rate limit config as described in https://www.npmjs.com/package/express-rate-limit |
| version           | String   |                                          | Your server's version printed out in `healthcheckPath` endpoint                    |