GithubHelp home page GithubHelp logo

hagopj13 / node-express-boilerplate Goto Github PK

View Code? Open in Web Editor NEW
6.4K 99.0 1.9K 1.58 MB

A boilerplate for building production-ready RESTful APIs using Node.js, Express, and Mongoose

License: MIT License

JavaScript 99.65% Dockerfile 0.20% Shell 0.14%
nodejs express mongoose mongodb boilerplate starter express-boilerplate node-boilerplate jest es6

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  avatar

node-express-boilerplate's Issues

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.

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.

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.

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.

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.

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.

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.

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

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.

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.

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.

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!

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

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.

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.

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.

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.

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.

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.

Use Lodash to minimum

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

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,
      });
    });

Documentation

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

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

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 } ] }

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

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.

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.

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!!"
}

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?

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?

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.

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.

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.

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.