Setup style linting in angular app.

We have already learned how to setup eslint and prettier in angular app. But this time we are going to setup style lint.

Check below link for quick reads.

stylelint.io

Lets get started, goto the angular project on the terminal and follow below steps.

Step 1

npm install --save-dev stylelint stylelint-config-sass-guidelines

Above command installs the stylelint and stylelint linter config (plugin) based on sass guidelines. The style linter (stylelint-config-sass-guidelines) or plugin will make sure we are linting our styles based on the sass guidelines.

Step 2

Create .stylelintrc.json (config file for stylelint) at the root of the angular app and add below configs.

{
  "extends": "stylelint-config-sass-guidelines",
  "rules": {
    "indentation": 4,
    "max-nesting-depth": null,
    "selector-max-compound-selectors": null,
    "color-named": null,
    "order/properties-alphabetical-order": null,
    "shorthand-property-no-redundant-values": null,
    "selector-pseudo-element-no-unknown": [
      true,
      {
        "ignorePseudoElements": "ng-deep"
      }
    ],
    "selector-no-qualifying-type": null,
    "selector-class-pattern": null
  }
}

.stylelintrc.json contains all our style linting rules and we have added few basic rules. Please check link for complete details about the plugin and all the rules.

Style lint setup for the angular project is complete. Now lets add few scripts to package.json file to make life bit better.

"stylelint": "stylelint \"src/**/*.scss\"",
"stylelint:fix": "stylelint \"src/**/*.scss\" --fix"
Style linting

Above screenshot shows our new scripts at package.json in action.

Checkout Github Repo for the sample project.

Create a simple number formatter pipe in Angular.

There will come a time in any application where you would want to format a given number. Like, formatting number to comma separated strings or to currency strings.

For example, you would want to format number 1000000 to 1,000,000 or number 1234567 to 1,234,567. You get what I mean.

Now to do this the right way in Angular we can create a pipe service. Using pipe as template expression we can transform our strings. A pipe would take a input value, process it and return a transformed value.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'nf',
})
export class NumberFormatterPipe implements PipeTransform {
    transform(value: number | undefined): string {
        if (!value) {
            return '';
        }
        return value.toString(10).replace(/\d(?=(\d{3})+\.)/g, '$&,');
    }
}

Above is a pipe which would transform our numbers. See below code sample to use this in a template.

<div class="text-primary fw-bold">
    {{ 1234567 | nf }}
</div>

Be sure to add the pipe class to the declarations of your Angular module before you start using it.

Checkout the Github repo for the complete project where this pipe is being used.

Export PDF in Angular with JSPDF.

In this post we are going to create an Angular 12 application and export a PDF file using jspdf library.

jspdf is a very niche library to generate PDFs in JavaScript. We can create PDFs for various types of forms or documents, like, work-orders, reports, invoices…etc. Checkout their website for various functionalities like using third party tools, browser print methods. Once we create the PDF we can also download it on the client-side.

Let us start with creating new Angular project.

ng new angular-jspdf-example
cd angular-jspdf-example/

Next, let’s also install Bootstrap CSS framework to create our table UI.

npm install bootstrap

Adding Bootstrap to Angular project…Open styles.scss (I have created my Angular project using SCSS) and add below line to import the Bootstrap styles.

// Note: We are importing all the bootstrap styles but you wouldn't need that.
@import './node_modules/bootstrap/scss/bootstrap';

Install jspdf.

npm install jspdf

We now have our Angular project ready with all the needed dependencies. Run the application to check if all is good!!.

ng serve

Cool, we are now ready to add code for our PDF file. Open app.component.html (template) file and add below code.

<div class="row">
  <div class="col-md-12">
    <table class="table table-striped">
      <thead>
      <tr>
        <th scope="col">#</th>
        <th scope="col">First</th>
        <th scope="col">Last</th>
        <th scope="col">Handle</th>
      </tr>
      </thead>
      <tbody>
      <tr *ngFor="let item of users">
        <th scope="row">{{ item.uid }}</th>
        <td>{{ item.first }}</td>
        <td>{{ item.last }}</td>
        <td>{{ item.handle }}</td>
      </tr>
      </tbody>
    </table>
    <button type="button" class="btn btn-primary" (click)="exportDataToPDF()">Export to PDF</button>
  </div>
</div>

Here we have a table (Note: grid and table styles are from Bootstrap) to show our users data and a button to export this table data to our PDF file. And in the component class file we are going to define the users data and also declare exportDataToPDF function to export the data to our PDF file.

Declare Users data, like below.

users = [
  {
    uid: '1',
    first: 'Mark',
    last: 'Otto',
    handle: '@mdo'
  },
  {
    uid: '2',
    first: 'Jacob',
    last: 'Thornton',
    handle: '@fat'
  },
  {
    uid: '2',
    first: 'Larry the Bird',
    last: 'Thornton',
    handle: '@twitter'
  }
];

We are using jspdf library functions, so import jspdf.

import { CellConfig, jsPDF } from 'jspdf';

Declare the ‘Export to PDF‘ handler function and necessary functions to construct table data from jspdf library.

exportDataToPDF() {
  // Creating a unique file name for my PDF
  const fileName = this.title.replace(' ', '_') + '_' + Math.floor((Math.random() * 1000000) + 1) + '.pdf';
  // Default export is a4 paper, portrait, using millimeters for units
  const doc = new jsPDF();
  doc.setFontSize(20);
  doc.setFont('helvetica', 'bold');
  const titleXPos = (doc.internal.pageSize.getWidth() / 2) - (doc.getTextWidth(this.title) / 2);
  doc.text(this.title, titleXPos, 20);
  doc.setFont('helvetica', 'normal');
  doc.setFontSize(14);
  doc.table(10, 35, this._getDataForPdfTable(), this._createHeadersForPdfTable([
    'uid', 'first', 'last', 'handle'
  ]), { autoSize: false });
  doc.save(fileName);
}

private _createHeadersForPdfTable(keys: string[]) {
  const result: CellConfig[] = [];
  for (let i = 0; i < keys.length; i += 1) {
    result.push({
      name: keys[i],
      prompt: keys[i],
      width: 55,
      align: 'center',
      padding: 10
    });
  }
  return result;
}

private _getDataForPdfTable() {
  const data = [];
  for (let i = 0; i < this.users.length; i++) {
    data.push({
      uid: this.users[i].uid,
      first: this.users[i].first,
      last: this.users[i].last,
      handle: this.users[i].handle,
    });
  }
  return data;
}

Now if you click the button you should see the PDF file exported with data from the table in table fashion.

But let’s take a moment to understand above functions.

  • const doc = new jsPDF(); will create a document instance for the PDF so we can call the required functions from the library to create our PDF file.
  • We are using few functions like setFontSize(), setFont(), text(), table() from the jspdf library to create the PDF as in required format.
  • save() function will export the PDF file.
  • Checkout the website for more details about the parameters these functions need.
Conclusion
  • We are able to create Angular project and install jspdf.
  • We are able to create table using jspdf and export PDF file.
  • We are able to set title, set font size.. etc for our PDF file.

Checkout complete code on Github and enjoy the demo.

Versions

  • Angular – ~12.2.0
  • Bootstrap – ^5.1.3
  • jspdf – ^2.4.0

Angular form validation using Bootstrap.

Before proceeding I request readers to have working knowledge about Angular (web application framework), keep a sample Angular project ready and also have Bootstrap installed on the project!.

Now thats out of the way let us get started with post. Consider we have a form like below.

Loan details form

And our form in template file will look like below.

<form [formGroup]="form">
    <h5 class="text-secondary">Loan details</h5>
    <div class="row py-3">
        <div class="col-md-3"></div>
        <div class="col-md-6">
            <div class="mb-3">
                <label for="loanAmount" class="form-label">Loan amount:</label>
                <input
                    type="text"
                    class="form-control"
                    id="loanAmount"
                    placeholder="Enter loan amount..."
                    formControlName="loanAmount"
                    required/>
            </div>
            <div class="mb-3">
                <label for="interestRate" class="form-label">Yearly interest rate:</label>
                <input
                    type="text"
                    class="form-control"
                    id="interestRate"
                    placeholder="Enter interest rate..."
                    formControlName="interestRate"/>
            </div>
            <div class="mb-3">
                <label for="loanTerm" class="form-label">Loan term (Years):</label>
                <input
                    type="text"
                    class="form-control"
                    id="loanTerm"
                    placeholder="Enter term..."
                    formControlName="loanTerm"
                    required/>
            </div>
            <div class="col-12">
                <button
                    class="btn btn-primary float-end"
                    type="submit"
                    (click)="validateForm()">
                    Calculate
                </button>
            </div>
        </div>
        <div class="col-md-3"></div>
    </div>
</form>

If you notice, we are actually using Reactive forms in Angular rather than Template-driven forms because these are more flexible, you will see. Feel free to read about it. And don’t forget to import the modules FormsModule, ReactiveFormsModule.

We have few directives like formGroup to initialise the form , formControlName in inputs to register the controls to form and on button click we are calling a function to take action. So let’s initiate the form and declare the validateForm function.

// Declare form group
this.form = this._fb.group({
                loanAmount: [
                    '',
                    Validators.compose([
                        Validators.required,
                        Validators.pattern(INTEGER_REGEXP),
                    ]),
                ],
                interestRate: [
                    '',
                    Validators.compose([
                        Validators.required,
                        Validators.pattern(FLOATING_REGEXP),
                    ]),
                ],
                loanTerm: [
                    '',
                    Validators.compose([
                        Validators.required,
                        Validators.pattern(INTEGER_REGEXP),
                    ]),
                ],
                });
// On submit button handler. Maybe you need to calculate the loan...
validateForm() {
        // Flag to indicate if uses clicked the Calculate button.
        this.isFormSubmitted = true;
        if (this.form.valid) { 
           // Do the loan calculation
        }
}
// Integer regexp
export const INTEGER_REGEXP = /^[0-9]*$/;

We have declared the form, added the needed validators (using the reactive forms we are able to correctly validate the state of the form input) and declared the Calculate button click handler. Also, take note we have added a pattern validator to the inputs.

Now it’s time to add the validations to form template using Bootstrap. To achieve this we are going to use few CSS selectors from Bootstrap CSS framework and others.

  • was-validated – This selector is needed on the form tag itself if the form is submitted.
  • is-invalid – We need to add this selector to form input controls if the form is submitted and if there are errors on the control.
  • invalid-feedback – We can use this selector to show the error message below the input control.
  • required – This directive is needed on the input controls, indicating the controls are required to have a value.
  • isFormSubmitted – flag declared in component class to indicate if user clicked the Calculate button to validate the form.

Let us put all these selectors together and complete the form.

<form
    class="needs-validation"
    [formGroup]="form"
    [class.was-validated]="isFormSubmitted">
    <h5 class="text-secondary">Loan details</h5>
    <div class="row py-3">
        <div class="col-md-3"></div>
        <div class="col-md-6">
            <div class="mb-3">
                <label for="loanAmount" class="form-label">Loan amount:</label>
                <input
                    type="text"
                    class="form-control"
                    id="loanAmount"
                    placeholder="Enter loan amount..."
                    formControlName="loanAmount"
                    [ngClass]="{
                        'is-invalid':
                            isFormSubmitted &&
                            form.controls['loanAmount'].errors
                    }"
                    required/>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['loanAmount'].errors?.required">
                    Loan amount cannot be empty
                </div>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['loanAmount'].errors?.pattern">
                    Loan amount must be a positive integer
                </div>
            </div>
            <div class="mb-3">
                <label for="interestRate" class="form-label">Yearly interest rate:</label>
                <input
                    type="text"
                    class="form-control"
                    id="interestRate"
                    placeholder="Enter interest rate..."
                    formControlName="interestRate"
                    [ngClass]="{
                        'is-invalid':
                            isFormSubmitted &&
                            form.controls['interestRate'].errors
                    }"
                    required/>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['interestRate'].errors?.required">
                    Interest rate cannot be empty
                </div>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['interestRate'].errors?.pattern">
                    Interest rate must be a positive integer or decimal
                </div>
            </div>
            <div class="mb-3">
                <label for="loanTerm" class="form-label">Loan term (Years):</label>
                <input
                    type="text"
                    class="form-control"
                    id="loanTerm"
                    placeholder="Enter term..."
                    formControlName="loanTerm"
                    [ngClass]="{
                        'is-invalid':
                            isFormSubmitted && form.controls['loanTerm'].errors
                    }"
                    required/>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['loanTerm'].errors?.required">
                    Loan term cannot be empty
                </div>
                <div
                    class="invalid-feedback"
                    *ngIf="form.controls['loanTerm'].errors?.pattern">
                    Loan term must be a positive integer
                </div>
            </div>
            <div class="col-12">
                <button
                    class="btn btn-primary float-end"
                    type="submit"
                    (click)="validateForm()">
                    Calculate
                </button>
            </div>
        </div>
        <div class="col-md-3"></div>
    </div>
</form>

Now we should have the validations working as expected. Test by clicking the Calculate button.

We are seeing the errors for inputs

Conclusion

We have completed the tutorial and we are able to achieve the following.

  • Create the form in Angular using reactive forms.
  • Implement Bootstrap in Angular and create form with Bootstrap.
  • Add validations to form and handle form submitted logic.

Check out full code at Github repo and enjoy the demo.

Versions:

  • Angular – 12
  • Bootstrap – 5

Create angular project in a specific version.

When we try to create a new angular project using the ng new command we get the angular project with version based on the global installed version of @angular/cli package.

So a quick fix here is to update the global @angular/cli package to latest version or which ever version you prefer so when you create a new angular project you get it with the required version.

npm uninstall -g @angular/cli
npm install -g @angular/cli@12.2.6

Using above commands we have updated our global @angular/cli version to 12.2.6. Now when you try to create a new angular project with ng new command you will get angular with version 12.2.6!!.

But what if you do not want to update the global @angular/cli version and still be able to create angular app with specific version (have your cake and eat it too). For this we can use npx command that executes npm package binaries.

First lets install npx globally.

npm install -g npx

Now we execute npx command with the -p parameter where we put a specific @angular/cli version. The last element of this statement is a command that creates an application on a specific @angular/cli version ng new [name of the project].

npx -p @angular/cli@12.2.6 ng new Angular12App

With above command we have a new angular project called Angular12App with version 12.2.0!!.

Below is the generated package.json content.

{
    "name": "angular12-app",
    "version": "0.0.0",
    "scripts": {
        "ng": "ng",
        "start": "ng serve",
        "build": "ng build",
        "watch": "ng build --watch --configuration development",
        "test": "ng test"
    },
    "private": true,
    "dependencies": {
        "@angular/animations": "~12.2.0",
        "@angular/common": "~12.2.0",
        "@angular/compiler": "~12.2.0",
        "@angular/core": "~12.2.0",
        "@angular/forms": "~12.2.0",
        "@angular/platform-browser": "~12.2.0",
        "@angular/platform-browser-dynamic": "~12.2.0",
        "@angular/router": "~12.2.0",
        "rxjs": "~6.6.0",
        "tslib": "^2.3.0",
        "zone.js": "~0.11.4"
    },
    "devDependencies": {
        "@angular-devkit/build-angular": "~12.2.6",
        "@angular/cli": "~12.2.6",
        "@angular/compiler-cli": "~12.2.0",
        "@types/jasmine": "~3.8.0",
        "@types/node": "^12.11.1",
        "jasmine-core": "~3.8.0",
        "karma": "~6.3.0",
        "karma-chrome-launcher": "~3.1.0",
        "karma-coverage": "~2.0.3",
        "karma-jasmine": "~4.0.0",
        "karma-jasmine-html-reporter": "~1.7.0",
        "prettier": "^2.4.1",
        "typescript": "~4.3.5"
    }
}

Conclusion

  • We learned how to create latest angular project by updating the global @angular/cli package to required version.
  • We have also learned to create latest angular project using npx.

Setup eslint and prettier in angular app.

Before we start with the steps to configure eslint and prettier I would encourage readers to spend sometime to know about these and why we need them. Check below links for the reads.

Lets start, goto the angular project on the terminal and follow below steps.

Step 1

npm install eslint --save-dev

Above command installs the eslint dev-dependency for the project.

Step 2

npx eslint --init

Above command is going to configure the angular project with eslint. Creates the configuration file called .eslintrc.json. See below screenshot for the process.

Steps on terminal

Step 3

Now we need to add two eslint plugins to make it work with angular project and also make the right configuration for eslint and prettier so they do not clash with one another and work seamlessly.

npm install eslint-config-prettier eslint-plugin-prettier prettier --save-dev

Above command installs the two plugins and also installs prettier. Feel free to read about the two plugins. Add the plugin config to the eslint config file (.eslintrc.json).

"extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:prettier/recommended"
    ],

Step 4

Create .prettierrc.json (config file for prettier) and add below code.

{
  "tabWidth": 4,
  "singleQuote": true,
  "trailingComma": "es5"
}

eslint and prettier setup is complete, now lets add few scripts to package.json.

"lint": "eslint .",
"lint:fix": "eslint --fix",
"format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json,html}' --config ./.prettierrc.json"

Go ahead and try these commands…format script will parse the code, fixes eslint issues along with prettier so they do not conflict with each other, this is what we want right!!.

Few assumptions:

  • You are angular developer.
  • You are quite good at using node and npm.
  • You already have angular application ready to setup eslint and prettier.

Check out Github repo for complete code.

Restrict user from entering non-numeric inputs using a directive in Angular.

There must a requirement in any application to allow only entering numbers in text inputs. This can be solved by creating a directive with onKeydown event handler. And then adding this directive to input element. See below code sample.

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[allowNumbersOnly]'
})

export class AllowNumbersOnlyDirective {

  constructor() {}

  /**
   * On keydown event handler.
   * Here we check if the input is number or not, if not we just ignore the input.
   * @param {KeyboardEvent} $event
   * @return {boolean}
   */
  @HostListener('keydown', ['$event'])
  onKeydown($event: KeyboardEvent) {
    if (!$event) {
      return true;
    }
    return ($event.keyCode > 95 && $event.keyCode < 106)
        || ($event.keyCode > 47 && $event.keyCode < 58)
        || ($event.keyCode > 36 && $event.keyCode < 41)
        || $event.keyCode === 8;

  }
}

We add this directive to input element like below.

<input type="text" allowNumbersOnly class="form-control">

When user tries to enter value other than number we simple ignore the input.

Also don’t forget to add the directive AllowNumbersOnlyDirective to the module declarations.

Few reads:

In webpack add a custom plugin to change the output of the template file.

For example say there is a scenario where you would like to change the title text in the template file. Our template file is a simple Html file with a title tag. For this you can create a custom plugin, in the plugin logic fetch the html content of the template file and use simple string function like replace to change the text.

First off, you should be familiar with webpack configuration file, check out – https://webpack.js.org/ to learn about webpack. Also plugins section to add or define a plugin https://webpack.js.org/configuration/plugins/.

Below is the code example.

module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      "template": "./index.html",
      "filename": "./newIndex.html",
      ...
    }),
    new CustomTemplatePlugin(),
  ],
};

In above code we are using the html-webpack-plugin to create the template (Html) file for the webpack bundle. After this plugin is finished our custom plugin is invoked (CustomTemplatePlugin) and here we are using the events (hooks) from the HtmlWebpackPlugin to fetch the Html content and make changes.

Below is code sample for the custom plugin.

const HtmlWebpackPlugin = require("html-webpack-plugin");

function CustomTemplatePlugin(options) {
  options = options || {};
}

CustomTemplatePlugin.prototype.apply = function(compiler) {
  compiler.hooks.compilation.tap("CustomTemplatePlugin", (compilation) => {
    HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync("CustomTemplatePlugin", (htmlPluginData, cb) => {
      const fileName = htmlPluginData.plugin.options["filename"];
      if (fileName && fileName.indexOf("index") > -1) {
        htmlPluginData.html = htmlPluginData.html.replace(/Loading.../, "Hello World");
      }
      cb(null, htmlPluginData);
    });
  });
};

In above code of our custom plugin we are using getHooks static function on the HtmlWebpackPlugin to add event listener (tabAsync). In the listener we are making the change as per our requirement. We are changing the text Loading… in the title tag of our Html file to Hello World.

Below is our simple Html file.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Loading...</title>
</head>
<body>

</body>
</html>

Now when you run the webpack we should see our new Html file in the bundle i.e newIndex.html and title tag should have Hello World.

Using Nintendo gaming console outside the allowed country.

I bought a Nintendo Switch lite few months back and I went through few hassles to setup my account on Nintendo and purchase games online. In this post I am going to explain how you can setup your account outside the allowed country / timezone by Nintendo and purchase games online.

When creating the account at Nintendo I noticed that my country India was not available to be selected in the dropdown list of countries. And I also noticed that while purchasing games online my credit card was not working (accepted) because my credit card is registered in India. So I had to fix these two issues so my Nintendo account is created and then I am able to purchase games online without the messy way of purchasing the game cards!!.

Fixing problem no. 1

Choose country and timezone

When creating your account you simply choose different country and the timezone of that country. I chose US as my country and America/New York as my timezone. Also you might need to select or enter the zip code of your selected region, I choose one location in US and its zip code. You can find zip codes online.

Fixing problem no.2

Add paypal account

After creating the Nintendo account goto account settings (select user on top-right corner and click Settings button), here you will see Shop Menu (see above) on left menu list panel. Click that button.

Now you are in Show Menu page. And here you will see options to add payment methods, you can add credit card or paypal account as a payment method and while purchasing the games the added account will be charged.

Credit card payment option did not work for me. So what I did is, I created a paypal account and in my paypal account added / linked my credit card. Now in Nintendo Shop Menu page I have added paypal as my payment method, it worked, I purchased lot of games online from Nintendo without any issues.

Nintendo is going to fetch the payment from your paypal account and the paypal account in turn bills your credit card. That is how it works.

Hope this post helps someone!.

Using bcrypt to store password in NodeJS.

Before starting the post I would like to assume few things:

  1. You have knowledge about NodeJS.
  2. You have setup sample NodeJS app using express and mongoDB

In case you have no idea what I am talking about then its best to know about them. Few links below will help.

Now let’s get to post…

Install bcryptjs:

$npm install bcryptjs

Hash your password:

Instead of storing the passwords in plain text we will use a method called hash in bcryptjs to generate hash. Hashing or password hashing is a process used to turn or transform your original plain string into another encrypted string. And this new string or hash is stored in DB. Even if someone finds the hash they won’t be able to login. This is the most secure way.

bcrypt.hash(myPassword, 10)

I would prefer you spend some time to read more about the plugin here.

Below is my sample code which creates a user and store the hash for password.

const bcrypt = require("bcryptjs");

exports.createUser = (req, res, next) => {
  bcrypt.hash(req.body.password, 10).then(hash => {
    const user = new User({
      username: req.body.username,
      password: hash,
      displayName: req.body.displayName,
      roles: req.body.roles,
    });
    user
        .save()
        .then(result => {
          res.status(201).json({
            message: "User created!",
            result: result
          });
        })
        .catch(err => {
          res.status(500).json({
            message: "Invalid authentication credentials!",
            error: err
          });
        });
  });
};

Below is the sample code for user login.

const bcrypt = require("bcryptjs");
const User = require("../models/user");

exports.userLogin = (req, res, next) => {
  let fetchedUser;
  User.findOne({ username: req.body.username })
      .then(user => {
        if (!user) {
          return res.status(401).json({
            message: "Auth failed"
          });
        }
        fetchedUser = user;
        return bcrypt.compare(req.body.password, user.password);
      })
      .then(result => {
        if (!result) {
          return res.status(401).json({
            message: "Authentication failed, please check your login credentials."
          });
        }
        res.status(200).json({
          message: "Login success."
        });
      })
      .catch(err => {
        return res.status(401).json({
          message: "Invalid authentication credentials!",
          error: err
        });
      });
};

Here we use the compare method in bcrypt to check if the entered user password matches with the stored password. It’s valid only if they match. You can also observe I am returning few responses with status codes at each step of the way.

Below is the schema for user just in case.

const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");

const userSchema = mongoose.Schema({
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  displayName: { type: String },
  roles: { type: [String], required: true }
});

userSchema.plugin(uniqueValidator);

module.exports = mongoose.model("User", userSchema);