Giter VIP home page Giter VIP logo

node-express-boilerplate's Introduction

RESTful API Node Server Boilerplate

Build Status Coverage Status PRs Welcome

A boilerplate/starter project for quickly building RESTful APIs using Node.js, Express, and Mongoose.

By running a single command, you will get a production-ready Node.js app installed and fully configured on your machine. The app comes with many built-in features, such as authentication using JWT, request validation, unit and integration tests, continuous integration, docker support, API documentation, pagination, etc. For more details, check the features list below.

Quick Start

To create a project, simply run:

npx create-nodejs-express-app <project-name>

Or

npm init nodejs-express-app <project-name>

Manual Installation

If you would still prefer to do the installation manually, follow these steps:

Clone the repo:

git clone --depth 1 https://github.com/hagopj13/node-express-boilerplate.git
cd node-express-boilerplate
npx rimraf ./.git

Install the dependencies:

yarn install

Set the environment variables:

cp .env.example .env

# open .env and modify the environment variables (if needed)

Table of Contents

Features

  • NoSQL database: MongoDB object data modeling using Mongoose
  • Authentication and authorization: using passport
  • Validation: request data validation using Joi
  • Logging: using winston and morgan
  • Testing: unit and integration tests using Jest
  • Error handling: centralized error handling mechanism
  • API documentation: with swagger-jsdoc and swagger-ui-express
  • Process management: advanced production process management using PM2
  • Dependency management: with Yarn
  • Environment variables: using dotenv and cross-env
  • Security: set security HTTP headers using helmet
  • Santizing: sanitize request data against xss and query injection
  • CORS: Cross-Origin Resource-Sharing enabled using cors
  • Compression: gzip compression with compression
  • CI: continuous integration with Travis CI
  • Docker support
  • Code coverage: using coveralls
  • Code quality: with Codacy
  • Git hooks: with husky and lint-staged
  • Linting: with ESLint and Prettier
  • Editor config: consistent editor configuration using EditorConfig

Commands

Running locally:

yarn dev

Running in production:

yarn start

Testing:

# run all tests
yarn test

# run all tests in watch mode
yarn test:watch

# run test coverage
yarn coverage

Docker:

# run docker container in development mode
yarn docker:dev

# run docker container in production mode
yarn docker:prod

# run all tests in a docker container
yarn docker:test

Linting:

# run ESLint
yarn lint

# fix ESLint errors
yarn lint:fix

# run prettier
yarn prettier

# fix prettier errors
yarn prettier:fix

Environment Variables

The environment variables can be found and modified in the .env file. They come with these default values:

# Port number
PORT=3000

# URL of the Mongo DB
MONGODB_URL=mongodb://127.0.0.1:27017/node-boilerplate

# JWT
# JWT secret key
JWT_SECRET=thisisasamplesecret
# Number of minutes after which an access token expires
JWT_ACCESS_EXPIRATION_MINUTES=30
# Number of days after which a refresh token expires
JWT_REFRESH_EXPIRATION_DAYS=30

# SMTP configuration options for the email service
# For testing, you can use a fake SMTP service like Ethereal: https://ethereal.email/create
SMTP_HOST=email-server
SMTP_PORT=587
SMTP_USERNAME=email-server-username
SMTP_PASSWORD=email-server-password
[email protected]

Project Structure

src\
 |--config\         # Environment variables and configuration related things
 |--controllers\    # Route controllers (controller layer)
 |--docs\           # Swagger files
 |--middlewares\    # Custom express middlewares
 |--models\         # Mongoose models (data layer)
 |--routes\         # Routes
 |--services\       # Business logic (service layer)
 |--utils\          # Utility classes and functions
 |--validations\    # Request data validation schemas
 |--app.js          # Express app
 |--index.js        # App entry point

API Documentation

To view the list of available APIs and their specifications, run the server and go to http://localhost:3000/v1/docs in your browser. This documentation page is automatically generated using the swagger definitions written as comments in the route files.

API Endpoints

List of available routes:

Auth routes:
POST /v1/auth/register - register
POST /v1/auth/login - login
POST /v1/auth/refresh-tokens - refresh auth tokens
POST /v1/auth/forgot-password - send reset password email
POST /v1/auth/reset-password - reset password
POST /v1/auth/send-verification-email - send verification email
POST /v1/auth/verify-email - verify email

User routes:
POST /v1/users - create a user
GET /v1/users - get all users
GET /v1/users/:userId - get user
PATCH /v1/users/:userId - update user
DELETE /v1/users/:userId - delete user

Error Handling

The app has a centralized error handling mechanism.

Controllers should try to catch the errors and forward them to the error handling middleware (by calling next(error)). For convenience, you can also wrap the controller inside the catchAsync utility wrapper, which forwards the error.

const catchAsync = require('../utils/catchAsync');

const controller = catchAsync(async (req, res) => {
  // this error will be forwarded to the error handling middleware
  throw new Error('Something wrong happened');
});

The error handling middleware sends an error response, which has the following format:

{
  "code": 404,
  "message": "Not found"
}

When running in development mode, the error response also contains the error stack.

The app has a utility ApiError class to which you can attach a response code and a message, and then throw it from anywhere (catchAsync will catch it).

For example, if you are trying to get a user from the DB who is not found, and you want to send a 404 error, the code should look something like:

const httpStatus = require('http-status');
const ApiError = require('../utils/ApiError');
const User = require('../models/User');

const getUser = async (userId) => {
  const user = await User.findById(userId);
  if (!user) {
    throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
  }
};

Validation

Request data is validated using Joi. Check the documentation for more details on how to write Joi validation schemas.

The validation schemas are defined in the src/validations directory and are used in the routes by providing them as parameters to the validate middleware.

const express = require('express');
const validate = require('../../middlewares/validate');
const userValidation = require('../../validations/user.validation');
const userController = require('../../controllers/user.controller');

const router = express.Router();

router.post('/users', validate(userValidation.createUser), userController.createUser);

Authentication

To require authentication for certain routes, you can use the auth middleware.

const express = require('express');
const auth = require('../../middlewares/auth');
const userController = require('../../controllers/user.controller');

const router = express.Router();

router.post('/users', auth(), userController.createUser);

These routes require a valid JWT access token in the Authorization request header using the Bearer schema. If the request does not contain a valid access token, an Unauthorized (401) error is thrown.

Generating Access Tokens:

An access token can be generated by making a successful call to the register (POST /v1/auth/register) or login (POST /v1/auth/login) endpoints. The response of these endpoints also contains refresh tokens (explained below).

An access token is valid for 30 minutes. You can modify this expiration time by changing the JWT_ACCESS_EXPIRATION_MINUTES environment variable in the .env file.

Refreshing Access Tokens:

After the access token expires, a new access token can be generated, by making a call to the refresh token endpoint (POST /v1/auth/refresh-tokens) and sending along a valid refresh token in the request body. This call returns a new access token and a new refresh token.

A refresh token is valid for 30 days. You can modify this expiration time by changing the JWT_REFRESH_EXPIRATION_DAYS environment variable in the .env file.

Authorization

The auth middleware can also be used to require certain rights/permissions to access a route.

const express = require('express');
const auth = require('../../middlewares/auth');
const userController = require('../../controllers/user.controller');

const router = express.Router();

router.post('/users', auth('manageUsers'), userController.createUser);

In the example above, an authenticated user can access this route only if that user has the manageUsers permission.

The permissions are role-based. You can view the permissions/rights of each role in the src/config/roles.js file.

If the user making the request does not have the required permissions to access this route, a Forbidden (403) error is thrown.

Logging

Import the logger from src/config/logger.js. It is using the Winston logging library.

Logging should be done according to the following severity levels (ascending order from most important to least important):

const logger = require('<path to src>/config/logger');

logger.error('message'); // level 0
logger.warn('message'); // level 1
logger.info('message'); // level 2
logger.http('message'); // level 3
logger.verbose('message'); // level 4
logger.debug('message'); // level 5

In development mode, log messages of all severity levels will be printed to the console.

In production mode, only info, warn, and error logs will be printed to the console.
It is up to the server (or process manager) to actually read them from the console and store them in log files.
This app uses pm2 in production mode, which is already configured to store the logs in log files.

Note: API request information (request url, response code, timestamp, etc.) are also automatically logged (using morgan).

Custom Mongoose Plugins

The app also contains 2 custom mongoose plugins that you can attach to any mongoose model schema. You can find the plugins in src/models/plugins.

const mongoose = require('mongoose');
const { toJSON, paginate } = require('./plugins');

const userSchema = mongoose.Schema(
  {
    /* schema definition here */
  },
  { timestamps: true }
);

userSchema.plugin(toJSON);
userSchema.plugin(paginate);

const User = mongoose.model('User', userSchema);

toJSON

The toJSON plugin applies the following changes in the toJSON transform call:

  • removes __v, createdAt, updatedAt, and any schema path that has private: true
  • replaces _id with id

paginate

The paginate plugin adds the paginate static method to the mongoose schema.

Adding this plugin to the User model schema will allow you to do the following:

const queryUsers = async (filter, options) => {
  const users = await User.paginate(filter, options);
  return users;
};

The filter param is a regular mongo filter.

The options param can have the following (optional) fields:

const options = {
  sortBy: 'name:desc', // sort order
  limit: 5, // maximum results per page
  page: 2, // page number
};

The plugin also supports sorting by multiple criteria (separated by a comma): sortBy: name:desc,role:asc

The paginate method returns a Promise, which fulfills with an object having the following properties:

{
  "results": [],
  "page": 2,
  "limit": 5,
  "totalPages": 10,
  "totalResults": 48
}

Linting

Linting is done using ESLint and Prettier.

In this app, ESLint is configured to follow the Airbnb JavaScript style guide with some modifications. It also extends eslint-config-prettier to turn off all rules that are unnecessary or might conflict with Prettier.

To modify the ESLint configuration, update the .eslintrc.json file. To modify the Prettier configuration, update the .prettierrc.json file.

To prevent a certain file or directory from being linted, add it to .eslintignore and .prettierignore.

To maintain a consistent coding style across different IDEs, the project contains .editorconfig

Contributing

Contributions are more than welcome! Please check out the contributing guide.

Inspirations

License

MIT

node-express-boilerplate's People

Contributors

abd1rahmane avatar abehnamfard avatar adrian-filipow avatar akshayjpatil avatar andreienache avatar bwyx avatar dependabot[bot] avatar engineervix avatar hagopj13 avatar icherya avatar mrdevx avatar mustafamohd avatar samkit5495 avatar shreyas-sriram avatar theumairriaz avatar thuvh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-express-boilerplate's Issues

Mongos timestamps absent from res json

In the mongo models, I enabled the {timestamps: true} however when logging out the response the updatedAt and createdAt values are present but never come across in the JSON response to the client I'm not sure why this is also true for paginated responses as well.

Unique email check for registration?

Shouldn't the /register function prevent users from signing up with the same email as another user? Currently I can register two users with the same email and different password, and because of the findOne() in the /login, only one user will be able to log in after that.

Caching Support

Hi,
Thank you for great boilerplate! How about adding support for caching in this to optimize the boilerplate more?
Nowadays it seems normal to provide caching out of the box.

Something like Redis with:

  • Auth in Cache for storing SET of valid JWT token of a user and removing any blacklisted tokens from the SET.
  • Storing user detail on Cache as well, and updating it value once user has been updated so that the JWT token with old user details can be avoided.

Thanks.

Relations (Populate)

Hi,

I want to load the relationship with the populate function inside the controller. I also use paginate.

const countPromise = this.countDocuments(filter).exec();
    const docsPromise = this.find(filter).populate('community').sort(sort).skip(skip).limit(limit).exec();

.populate('community')

To do this, I edited the plugin part. But I don't want this to work on all models. how can I define a relationship through the controller?

Best regards.

How to use auth() method?

Hi,
I need to use auth () to activate users who log in with tokens.
In this case, we can take the user as req.user in the controller and perform the operation.

But in any case, I have to check the req.user information.

my scenario is as follows:

Without auth (), a route is broadcasting publicly.
The user can view this route without logging in.
I want to send an additional field if it is logged in and has a relationship with the content it looks at /ep/54e...
I want to do something like "You have purchased this product before, you don't need to buy it again". /ep/54e.../buy
I have to define auth () under any condition to get the req.user information. but I also want my route to be public.

How can I verify if the request is from a verified user?
How should I use the auth () alternative methods?

Thank you.

Add support for Google auth

Allow authentication with Google sign-in. This includes registering the user (on first login) and integrating with the current access token setup.

Get/set model results (mutators)

Hi,
I want to wrap image url in schema results. Because I saved my file name as public/x.jpg.
I need to show url like this : http://localhost:3000/public/x.jpg.

My temprorary bad solution

theaterSchema.methods.transform = function () {
  const theater = this;

  let data = pick(theater.toJSON(), ['id', 'title', 'body', 'image', 'tags']);
  if(!data.image){
    return data;
  }else {
    return Object.assign({}, data, {
      image: 'http://localhost:3000/' + data.image
    })
  }

};

But I dont set with pagination for new pagination feature.
I wonder your best practice.

Best regards.

Docs Swagger All docs doesnt appear(Answer)

Everything is good but, you have to remove the path lines

For example your code:

 @swagger
   path
    /auth/register:
      post:
        summary: Register as user
        tags: [Auth]
        requestBody:
          required: true
          content:

For example new version swag js doc:

 @swagger
   /auth/register:
    post:
     summary: Register as user
      tags: [Auth]
       requestBody:
         required: true
         content:

Note: Close the issue like solved.

Reset Password throwing 401 error

Hi

Thanks for the crisp boilerplate code orchestration.

Regarding the Reset Password feature, the API throws unauthorized error. Could you direct me if I'm missing something?

thanks
~ bw

Registration bug

Hi @hagopj13, Thank you for your effort.

I could easily create admin users sending email, password and role to the registration endpoint because it reads the hole body when it only take email and password.

Add web-push-notification feature

Push notification is a need of every developer. It will be helpful if you can add this feature in your boilerplate. Thanks in advance.

Socket support

Hi,
Excellent boilerplate! How about adding support for sockets in this to make a more complete boilerplate?
Also instructions on deployment using Docker/native would be very helpful.
Thanks.

Swagger UI not showing operations

First of all, thanks for this awesome boilerplate!

I have downloaded the repo in order to check it out and I have found that the operations grouped by tags on Swagger UI are not showing:

image

When you click on both Auth or Users tags, nothing appears.

This is right after cloning the repo and doing an npm install command.

Not sure if this is related with the configuration (as it seems correct) or the swagger packages. I have tried several browsers with same outcome. No console error message or watsoever.

email is required

I tried to post an request to /v1/auth/register but it error as - message: "email" is required. However, I have given an valid email eventhough its getting rejected. why is this happening ? please let know your thoughts on this. Thanks

{
"name": "admin",
"email": "[email protected]",
"password": "password!!"
}

Use Lodash to minimum

Instead of using pick method of lodash, you can use the destructuring concept of javascript core.
Keeping the dependencies minimum.

Throwing ApiError from service

HI @hagopj13, first of all thank you for this boilerplate, it's very useful.
I'm not an expert, but I have a question related to error throwing. Is it a good practice to throw specific error like ApiError from the service layer? I always thought that this kind of error belong to the controller, while the service shouldn't know that the caller is talking using HTTP. Am i wrong? Thank you.

Why not separate mongoose plugins into a diff repository?

Also, loved the idea for private fields, but it doesn't work with nested fields. I've made a change on the project I've started from this boilerplate, something like

if (path.includes('.')) {
  const [a, b] = path.split('.');
  delete ret[a][b];
} else {
  delete ret[path];
}

it obviously only work for 1 level of nesting, but changing to allow multiple would be fairly easy. Do you think this is something you'd like to see added to the plugin in this repo, or perhaps in a separated package?

Proposal: let's use `rfr`

rfr allows to make all requires from the root of the project, reducing the cognitive load by avoiding to have in mind where is a file to realize where a required module lives.

From it's README page:

allows you to require modules in your project with rfr('lib/module1.js') instead of something like require ('../../lib/module1.js')

I offer myself to make such a change should be the idea accepted.

I want to use Mongo Operators in pagination

Hi,
I want to improve my queries with parameters. I have to send an array through parameters and query accordingly. I did it myself manually, but I wonder how to do this without breaking the structure.

As an example, what I want to do is filter a content that has an array. I have to shoot content that has the values I have specified in this way. The query I made is below

endpoint?limit=10&page=1&tags[]=foo&tags[]=bar

When I change the codes in the paginate plugin as follows, what I want to do is exactly. How can I make my query in a simple and beautiful way?

 schema.statics.paginate = async function(filter, options) {
  ...
    const countPromise = this.countDocuments(filter).exec();
    const docsPromise = this.find({tags: { $in : ['foo', 'bar'] }} ).populate(options.relations).sort(sort).skip(skip).limit(limit).exec();

Thanks.

Unable to test routes

I am trying to test the /users routes with Postman, but it is returning 404.
Is there a list of the available routes?
I am trying this - GET localhost:3000/v1/users/getUsers/:userId , but it returns 404

Paginate

Hi, thanx for this repo, I have a question. How about paginate. Do you have a bast practice?
I have to set page_number and page count meta.

{ "total": 50, "per_page": 15, "current_page": 1, "last_page": 4, "first_page_url": "http://laravel.app?page=1", "last_page_url": "http://laravel.app?page=4", "next_page_url": "http://laravel.app?page=2", "prev_page_url": null, "path": "http://laravel.app", "from": 1, "to": 15, "data":[ { // Result Object }, { // Result Object } ] }

Add Support for debug mode

Debugging via debug | Instead of inserting and deleting console.log you can replace it with the debug function and just leave it there. You can then selectively debug portions of your code by setting DEBUG env variable. If DEBUG env variable is not set, nothing is displayed to the console.

Test assertion may be unpredictable

HI, i was looking at this test:

test('should return 200 and apply the default query options', async () => {
      await insertUsers([userOne, userTwo, admin]);

      const res = await request(app)
        .get('/v1/users')
        .set('Authorization', `Bearer ${adminAccessToken}`)
        .send()
        .expect(httpStatus.OK);

      expect(res.body).toEqual({
        results: expect.any(Array),
        page: 1,
        limit: 10,
        totalPages: 1,
        totalResults: 3,
      });
      expect(res.body.results).toHaveLength(3);
      expect(res.body.results[0]).toEqual({
        id: userOne._id.toHexString(),
        name: userOne.name,
        email: userOne.email,
        role: userOne.role,
      });
    });

More in detail at this part:

expect(res.body.results[0]).toEqual({
        id: userOne._id.toHexString(),
        name: userOne.name,
        email: userOne.email,
        role: userOne.role,
      });
    });

It seems to me that you can't be sure that the first element is userOne, because there is no default sorting provided and mongo don't ensures a default sort. Am I wrong?
Maybe you can do:

expect(res.body.results).toContainEqual({
        id: userOne._id.toHexString(),
        name: userOne.name,
        email: userOne.email,
        role: userOne.role,
      });
    });

Typescript version of the boilerplate

Thanks for creating this repo. It's helpful to fire a server up without spending too much time. It would be nicer to have the same repo for the typescript(I know it already supports it but could be good not to spend time converting it from javascript).

Anyway, Good Job!

Documentation

Is there some kind of Documentation that reaches beyond the installation process?

Auth Middleware does not distinguish type of JWT Token

I am new to Express/NodeJS, so please let me know if I am understanding this incorrectly.

This boilerplate uses 3 different types of JWTs namely Access Tokens, Refresh Tokens and Reset Password Token.

All types of tokens are created with the same content and structure.
const generateToken = (userId, expires, secret = config.jwt.secret) => { const payload = { sub: userId, iat: moment().unix(), exp: expires.unix(), }; return jwt.sign(payload, secret); };

Also the auth middleware does not differenciate between the different types:

`const verifyCallback = (req, resolve, reject, requiredRights) => async (
err,
user,
info
) => {
if (err || info || !user) {
return reject(new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
}
req.user = user;

if (requiredRights.length) {
const userRights = roleRights.get(user.role);
const hasRequiredRights = requiredRights.every((requiredRight) =>
userRights.includes(requiredRight)
);

if (!hasRequiredRights && req.params.userId !== user.id) {
  return reject(new ApiError(httpStatus.FORBIDDEN, 'Forbidden'));
}

}

resolve();
};

const auth = (...requiredRights) => async (req, res, next) => {
return new Promise((resolve, reject) => {
passport.authenticate(
'jwt',
{ session: false },
verifyCallback(req, resolve, reject, requiredRights)
)(req, res, next);
})
.then(() => next())
.catch((err) => next(err));
};

module.exports = auth;`

This leads to all kinds of tokens being valid access tokens.

I am not sure if this is even a problem for security or if it is the intended behavior. It still seems odd to me.
Let me know if I am missing anything.

Can't Access Swagger UI When It's Behind Proxy

Hi,
I'm starting a project on top of this boilerplate, although it's pretty great and helpful. But I have a little problem with swagger UI.
it works fine on my local machine, but when it comes to deployment, swagger UI is not loading, and I get these errors:

image

Swagger UI not adding Authorization to header

Hi,
I am trying to access APIs using swagger API,
Steps i did:

  • I have created a user with admin role, i can see data in mongoDb.

  • after plain installation I tried to get user but it says
    error: Error: Please authenticate at /src/middlewares/auth.js:10:19

where is to add the token generated while user login? Thanks.

Style proposal: async/await vs .then()

I've seen there's a mix in the code where sometimes async/await is used and sometimes somePromise.then().catch(). I'd like to propose to homogenize the code by using always async/await, and I offer myself to make the PR should the proposal be accepted.

Also, if it's accepted, it should be added to the contribution guideline.

Array sortBy

Hi,

I want to use paginate with search and order params query. But this repo not supported this. I was tried multiple sortBy like this.

My url /endpoint?limit=5&page=1&sortBy[]=stars:desc&sortBy[]=price:asc
The positions of the ranking parameters affect the results. whichever applies first.

But I wonder is this best practice?

Just modified this area

schema.statics.paginate = async function(filter, options) {
    const sort = [];

    if (options.sortBy) {
      Object.entries(options.sortBy)
        .forEach(([key, value]) => {
          const parts = value.split(':');
          sort.push([parts[0], parts[1] === 'desc' ? -1 : 1]);
        });
    }

Whole code models/plugins.js

const paginate = (schema) => {
  /**
   * @typedef {Object} QueryResult
   * @property {Document[]} results - Results found
   * @property {number} page - Current page
   * @property {number} limit - Maximum number of results per page
   * @property {number} totalPages - Total number of pages
   * @property {number} totalResults - Total number of documents
   */
  /**
   * Query for documents with pagination
   * @param {Object} [filter] - Mongo filter
   * @param {Object} [options] - Query options
   * @param {string} [options.sortBy] - Sort option in the format: sortField:(desc|asc)
   * @param {number} [options.limit] - Maximum number of results per page (default = 10)
   * @param {number} [options.page] - Current page (default = 1)
   * @returns {Promise<QueryResult>}
   */
  schema.statics.paginate = async function(filter, options) {
    const sort = [];

    if (options.sortBy) {
      Object.entries(options.sortBy)
        .forEach(([key, value]) => {
          const parts = value.split(':');
          sort.push([parts[0], parts[1] === 'desc' ? -1 : 1]);
        });
    }

    const limit = options.limit && parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 10;
    const page = options.page && parseInt(options.page, 10) > 0 ? parseInt(options.page, 10) : 1;
    const skip = (page - 1) * limit;

    const countPromise = this.countDocuments(filter).exec();
    const docsPromise = this.find(filter).populate('community').sort(sort).skip(skip).limit(limit).exec();

    return Promise.all([countPromise, docsPromise]).then((values) => {
      const [totalResults, results] = values;
      const totalPages = Math.ceil(totalResults / limit);
      const result = {
        results,
        page,
        limit,
        totalPages,
        totalResults
      };
      return Promise.resolve(result);
    });
  };
};

I took as an example : https://stackoverflow.com/a/13587343/4329812

Best regards.

Auth middleware: httpStatus.FORBIDDEN will never be executed

Hello,

I was analyzing the code and apparently the following code will never be executed:

middleware/auth.js

if (!hasRequiredRights && req.params.userId !== user.id) {
            return reject(new ApiError(httpStatus.FORBIDDEN, 'Forbidden'));
        }

It should be

if (!hasRequiredRights || req.params.userId !== user.id) {
            return reject(new ApiError(httpStatus.FORBIDDEN, 'Forbidden'));
        }

Or am I wrong? Currently with the && operator the condition will always return false

req.user object

Hello and thanks once again! the req.user object is showing undefined. Whats the most convenient way to get hold of this to use in my .services.js? Do i use the verifyToken function then pull it from there or is it being attached somewhere else?

Sharp pre-built with docker

Hi,

I need to add sharp, I know, this is not issue. But The solutions I found are to change docker images, I cant do this.

I have a problem with sharp linux libs.

No error on my workspace without docker. But if deployment via docker, I get error.

Error: 'linux-x64' binaries cannot be used on the 'linuxmusl-x64' platform. Please remove the 'node_modules/sharp' directory and run 'npm install' on the 'linuxmusl-x64' platform.

I am tried this solition. But, there are node permission problems. I didnt install sharp on docker. I need to install sharp on docker for pre-built proccess.

There are some docker images there for sharp with alpine. But I dont want to use them https://hub.docker.com/r/gazf/alpine-sharp
Relation Issue :
https://stackoverflow.com/questions/57932529/linux-x64-binaries-cannot-be-used-on-the-linuxmusl-x64-platform-error

Can I use another docker image? Do you think that makes sense?
If you encounter such a problem, what would your solution be?

I think many projects need image manipulation. Do you think it makes sense to add this to this repository?

Best regards.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.