├── .angular-cli.json
├── .babelrc
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.e2e.json
├── karma.conf.js
├── nodemon.json
├── package.json
├── protractor.conf.js
├── proxy.conf.json
├── server
├── api
│ ├── api.js
│ └── v1
│ │ ├── api-schemas.js
│ │ ├── index.js
│ │ └── user
│ │ ├── user.api.js
│ │ └── user.api.schemas.js
├── app.js
├── config
│ └── config.js
├── db
│ └── db.js
├── models
│ └── user.model.js
└── seed
│ ├── seed.data.js
│ └── seed.js
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── components
│ │ └── header
│ │ │ ├── header.component.html
│ │ │ ├── header.component.scss
│ │ │ ├── header.component.spec.ts
│ │ │ └── header.component.ts
│ ├── containers
│ │ └── home
│ │ │ ├── home.component.html
│ │ │ ├── home.component.scss
│ │ │ ├── home.component.spec.ts
│ │ │ └── home.component.ts
│ └── users
│ │ ├── add-user-modal
│ │ ├── add-user-modal.component.html
│ │ ├── add-user-modal.component.scss
│ │ ├── add-user-modal.component.spec.ts
│ │ └── add-user-modal.component.ts
│ │ ├── user-list-item
│ │ ├── user-list-item.component.html
│ │ ├── user-list-item.component.scss
│ │ ├── user-list-item.component.spec.ts
│ │ └── user-list-item.component.ts
│ │ ├── users-list
│ │ ├── users-list.component.html
│ │ ├── users-list.component.scss
│ │ ├── users-list.component.spec.ts
│ │ └── users-list.component.ts
│ │ ├── users.component.html
│ │ ├── users.component.scss
│ │ ├── users.component.spec.ts
│ │ ├── users.component.ts
│ │ ├── users.models.ts
│ │ ├── users.module.ts
│ │ ├── users.routes.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
├── assets
│ ├── .gitkeep
│ ├── angular.png
│ ├── fastify.png
│ └── ng-fastify.png
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.scss
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "angular-fastify-starter"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "../node_modules/bootstrap/dist/css/bootstrap.min.css",
23 | "../node_modules/ng2-toastr/bundles/ng2-toastr.min.css",
24 | "styles.scss"
25 | ],
26 | "scripts": [],
27 | "environmentSource": "environments/environment.ts",
28 | "environments": {
29 | "dev": "environments/environment.ts",
30 | "prod": "environments/environment.prod.ts"
31 | }
32 | }
33 | ],
34 | "e2e": {
35 | "protractor": {
36 | "config": "./protractor.conf.js"
37 | }
38 | },
39 | "lint": [
40 | {
41 | "project": "src/tsconfig.app.json",
42 | "exclude": "**/node_modules/**"
43 | },
44 | {
45 | "project": "src/tsconfig.spec.json",
46 | "exclude": "**/node_modules/**"
47 | },
48 | {
49 | "project": "e2e/tsconfig.e2e.json",
50 | "exclude": "**/node_modules/**"
51 | }
52 | ],
53 | "test": {
54 | "karma": {
55 | "config": "./karma.conf.js"
56 | }
57 | },
58 | "defaults": {
59 | "styleExt": "scss",
60 | "component": {}
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "env" ]
3 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | testem.log
34 | /typings
35 | yarn-error.log
36 |
37 | # e2e
38 | /e2e/*.js
39 | /e2e/*.map
40 |
41 | # System Files
42 | .DS_Store
43 | Thumbs.db
44 |
45 | # Server logs
46 | server/logs
47 |
48 | # Logs
49 | logs
50 | *.log
51 | npm-debug.log*
52 | yarn-debug.log*
53 | yarn-error.log*
54 |
55 | # Runtime data
56 | pids
57 | *.pid
58 | *.seed
59 | *.pid.lock
60 |
61 | # Directory for instrumented libs generated by jscoverage/JSCover
62 | lib-cov
63 |
64 | # Coverage directory used by tools like istanbul
65 | coverage
66 |
67 | # nyc test coverage
68 | .nyc_output
69 |
70 | # node-waf configuration
71 | .lock-wscript
72 |
73 | # Compiled binary addons (http://nodejs.org/api/addons.html)
74 | build/Release
75 |
76 | # Typescript v1 declaration files
77 | typings/
78 |
79 | # Optional npm cache directory
80 | .npm
81 |
82 | # Optional eslint cache
83 | .eslintcache
84 |
85 | # Optional REPL history
86 | .node_repl_history
87 |
88 | # Output of 'npm pack'
89 | *.tgz
90 |
91 | # Yarn Integrity file
92 | .yarn-integrity
93 |
94 | # dotenv environment variables file
95 | .env
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Ahsan Ayaz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AngularFastifyStarter
2 |
3 |
4 |
5 | ## Technology Stack Used
6 |
7 | - [Angular ( 4.x )](angular.io)
8 | - [Bootstrap 4](getbootstrap.com)
9 | - [NgxBootstrap](https://www.npmjs.com/package/ng2-toastr)
10 | - [Ng2Toastr](valor-software.com/ngx-bootstrap/#/)
11 | - [Fastify](https://github.com/fastify/fastify)
12 | - [FastifyMongoDb](https://github.com/fastify/fastify-mongodb)
13 | - [AngularCLI](https://github.com/angular/angular-cli)
14 | - [Webpack](https://github.com/webpack/webpack)
15 | - [Pino](https://github.com/pinojs/pino)
16 | - [Nodemon](https://nodemon.io/)
17 | - [Concurrently](https://www.npmjs.com/package/concurrently)
18 |
19 | ## Features Covered
20 |
21 | ### Front End
22 | - Angular Smart vs Dumb Components
23 | - Usage of HttpModule
24 | - Showing Toaster alerts via Ng2Toaster
25 | - Lazy Loading Routes (User route)
26 | - Usage of NgxBootstrap i.e. Modal & Collapse (for menu on small screen widths)
27 |
28 | ### Back End
29 | - Fastify as backend framwork
30 | - Fastify Mongo Db extension to connect to mongo db
31 | - Usage of fastify logging
32 | - Writing log stream to file using pino-tee
33 | - Usage of fastify routes, prefixing (e.g. for versioning)
34 | - Usage of fastify schemas for responses and request params/body etc validation
35 | - Usage of babel & babel-preset to be able to use ES6 for backend as well
36 | - Using nodemon & concurrently for serving both front-end and client end while watching too
37 |
38 | ## Install / Development
39 |
40 | ```bash
41 | git clone https://github.com/AhsanAyaz/angular-fastify-starter
42 | cd angular-fastify-starter
43 |
44 | # Install dependencies using npm or yarn
45 | npm install
46 | #or
47 | yarn
48 |
49 | #make sure your mongodb instance is running. If not, do
50 | mongod
51 | #or if required,
52 | sudo mongod
53 |
54 | # start the magic
55 | npm run start
56 | # this will start both client and server
57 | # Client default url: http://localhost:4200
58 | # Server ( fastify app ) API default url: http://localhost:4500
59 | ```
60 |
61 | ## Proxy for api calls
62 |
63 | The api for the web front-end is proxied using `proxy.conf.json`. Modify as per requirement.
64 |
65 | ## Seed Data
66 |
67 | The server config, i.e. `server/config/config.js` contains the app config for server.
68 | It has a property `seedData` which is by default set to `true`. If it remains `true`, every time the server will start or restart,
69 | the data from `users` collection will be removed and fresh Seed data will be inserted. To disable this, just make `seedData` false.
70 |
71 | ## Client Code scaffolding
72 |
73 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
74 |
75 | ## Client Build
76 |
77 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
78 |
79 | ## Running client unit tests
80 |
81 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
82 |
83 | ## Running client end-to-end tests
84 |
85 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
86 | Before running the tests make sure you are serving the app via `ng serve`.
87 |
88 | # Contribute
89 |
90 | Please feel free to fork, star and contribute to the repository. There surely is a lot of room for improvement for this starter :)
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AngularFastifyStarterPage } from './app.po';
2 |
3 | describe('angular-fastify-starter App', () => {
4 | let page: AngularFastifyStarterPage;
5 |
6 | beforeEach(() => {
7 | page = new AngularFastifyStarterPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AngularFastifyStarterPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "verbose": false,
4 | "ignore": [
5 | ".git",
6 | "node_modules/**/node_modules"
7 | ],
8 | "execMap": {
9 | "js": "babel-node"
10 | },
11 | "watch": [
12 | "server"
13 | ],
14 | "env": {
15 | "NODE_ENV": "development"
16 | },
17 | "ext": "js json"
18 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-fastify-starter",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "node_modules/.bin/concurrently --kill-others \"npm run server:run\" \"ng serve --pc proxy.conf.json --progress false\"",
8 | "build": "ng build",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e",
12 | "server:run": "nodemon --delay 5 server/app.js"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^4.0.0",
17 | "@angular/common": "^4.0.0",
18 | "@angular/compiler": "^4.0.0",
19 | "@angular/core": "^4.0.0",
20 | "@angular/forms": "^4.0.0",
21 | "@angular/http": "^4.0.0",
22 | "@angular/platform-browser": "^4.0.0",
23 | "@angular/platform-browser-dynamic": "^4.0.0",
24 | "@angular/router": "^4.0.0",
25 | "bootstrap": "4.0.0-beta",
26 | "core-js": "^2.4.1",
27 | "fastify": "^0.26.2",
28 | "fastify-mongodb": "^0.1.0",
29 | "ng2-toastr": "^4.1.2",
30 | "ngx-bootstrap": "^1.9.3",
31 | "pino-tee": "^0.2.0",
32 | "rxjs": "^5.4.1",
33 | "zone.js": "^0.8.14"
34 | },
35 | "devDependencies": {
36 | "@angular/cli": "1.2.7",
37 | "@angular/compiler-cli": "^4.0.0",
38 | "@angular/language-service": "^4.0.0",
39 | "@types/jasmine": "~2.5.53",
40 | "@types/jasminewd2": "~2.0.2",
41 | "@types/node": "~6.0.60",
42 | "babel-cli": "^6.26.0",
43 | "babel-preset-env": "^1.6.0",
44 | "codelyzer": "^3.1.2",
45 | "concurrently": "^3.5.0",
46 | "jasmine-core": "~2.6.2",
47 | "jasmine-spec-reporter": "~4.1.0",
48 | "karma": "~1.7.0",
49 | "karma-chrome-launcher": "~2.1.1",
50 | "karma-cli": "~1.0.1",
51 | "karma-coverage-istanbul-reporter": "^1.2.1",
52 | "karma-jasmine": "~1.1.0",
53 | "karma-jasmine-html-reporter": "^0.2.2",
54 | "nodemon": "^1.12.0",
55 | "protractor": "~5.1.2",
56 | "ts-node": "~3.0.4",
57 | "tslint": "~5.3.2",
58 | "typescript": "~2.3.3"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | "target": "http://localhost:4500",
4 | "secure": false
5 | }
6 | }
--------------------------------------------------------------------------------
/server/api/api.js:
--------------------------------------------------------------------------------
1 | import v1Api from './v1';
2 |
3 | export default (app) => {
4 | v1Api(app); // registering v1 api
5 | }
--------------------------------------------------------------------------------
/server/api/v1/api-schemas.js:
--------------------------------------------------------------------------------
1 | // API VERSION LEVEL SCHEMAS CAN RESIDE HERE
--------------------------------------------------------------------------------
/server/api/v1/index.js:
--------------------------------------------------------------------------------
1 | import userApi from './user/user.api'
2 | // import more apis here
3 |
4 | export default function(app) {
5 | // register all your v1 apis here
6 | app.register(userApi, { prefix: '/api/v1' })
7 | }
--------------------------------------------------------------------------------
/server/api/v1/user/user.api.js:
--------------------------------------------------------------------------------
1 | import USER_SCHEMAS from './user.api.schemas';
2 | import assert from 'assert';
3 | let db, userCollection, ObjectId;
4 | const routeOpts = {
5 | schema: {
6 |
7 | }
8 | }
9 |
10 | // USER API HANDLERS
11 |
12 | const getUsers = (request, reply) => {
13 | userCollection.find().toArray((err, users) => {
14 | if(err) reply.send({ error: err });
15 | else{
16 | reply.send({ users })
17 | }
18 | })
19 | }
20 |
21 | const addUser = (request, reply) => {
22 | const user = request.body;
23 | userCollection.insert(user, (err, result) => {
24 | assert.equal(err, null);
25 | assert.equal(1, result.result.n);
26 | reply.send({ user: result.ops[0] })
27 | })
28 | }
29 |
30 | const deleteUser = (request, reply) => {
31 | request.log.error(request.params);
32 | userCollection.remove({
33 | _id: new ObjectId(request.params.userId)
34 | }, (err, result) => {
35 | assert.equal(err, null);
36 | assert.equal(1, result.result.n);
37 | reply.send({ result: result.result })
38 | })
39 | }
40 |
41 | const getUserById = (request, reply) => {
42 | userCollection.findOne({
43 | _id: new ObjectId(request.params.userId)
44 | }, (err, result) => {
45 | assert.equal(err, null);
46 | reply.send({ user: result })
47 | })
48 | }
49 |
50 |
51 | export default function (app, options, next) {
52 | db = app.mongo.db;
53 | ObjectId = app.mongo.ObjectId;
54 | userCollection = db.collection('users');
55 |
56 | // ROUTES
57 |
58 | // get users
59 | app.get('/user', Object.assign(routeOpts, {
60 | schema: {
61 | response: USER_SCHEMAS.USERS_RESPONSE
62 | }
63 | }), getUsers);
64 |
65 | // get user by id
66 | app.get('/user/:userId', {} , getUserById);
67 |
68 | // add user
69 | app.post('/user', Object.assign(routeOpts, {
70 | schema: {
71 | body: USER_SCHEMAS.CREATE_USER_BODY,
72 | response: USER_SCHEMAS.USER_RESPONSE
73 | }
74 | }), addUser);
75 |
76 | // delete user
77 | app.delete('/user/:userId', {} , deleteUser);
78 |
79 | next()
80 | }
--------------------------------------------------------------------------------
/server/api/v1/user/user.api.schemas.js:
--------------------------------------------------------------------------------
1 | import UserModel from '../../../models/user.model';
2 |
3 | export const USERS_RESPONSE = { // multi users response
4 | 200: {
5 | type: 'object',
6 | properties: {
7 | users: { type: 'array' , items: UserModel }
8 | }
9 | },
10 | '4xx': {
11 | type: 'object',
12 | properties: {
13 | error: { type: 'string' },
14 | message: { type: 'string' }
15 | }
16 | }
17 | }
18 |
19 | export const USER_RESPONSE = { // single user response
20 | 200: {
21 | type: 'object',
22 | properties: {
23 | user: UserModel
24 | }
25 | },
26 | '4xx': {
27 | type: 'object',
28 | properties: {
29 | error: { type: 'string' },
30 | message: { type: 'string' }
31 | }
32 | }
33 | }
34 |
35 | export const CREATE_USER_BODY = { // create user body validation
36 | type: 'object',
37 | properties: {
38 | email: { type: 'string' },
39 | password: { type: 'string' }
40 | }
41 | }
42 |
43 | export const DELETE_USER_PARAMS = { // delete user params validation
44 | userId: { type: 'string' }
45 | }
46 |
47 | export const USER_SCHEMAS = {
48 | USERS_RESPONSE,
49 | USER_RESPONSE,
50 | CREATE_USER_BODY,
51 | DELETE_USER_PARAMS
52 | };
53 |
54 | export default USER_SCHEMAS;
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | import fastify from 'fastify';
2 | import registerDB from './db/db';
3 | import api from './api/api';
4 | import insertSeedData from './seed/seed';
5 | import pino from 'pino';
6 | import tee from 'pino-tee';
7 | import fs from 'fs';
8 | import appConfig from './config/config';
9 |
10 | // Set up stream for logging
11 | const stream = tee(process.stdin);
12 | stream.tee(fs.createWriteStream('server/logs/info'), line => line.level >= 20)
13 | stream.pipe(process.stdout);
14 |
15 | // Register logger for fastify app
16 | const app = fastify({
17 | logger: pino(stream)
18 | });
19 |
20 |
21 | // register database (mongo setup for this example)
22 | registerDB(app)
23 | .then(() => {
24 | // when registered, initiate the api
25 | api(app);
26 |
27 | if(appConfig.seedData){ // insert seed data if configured in the appConfig
28 | insertSeedData(app);
29 | }
30 | })
31 | .catch(err=>{
32 | console.log(err);
33 | });
34 |
35 |
36 | // Run the server!
37 | app.listen(appConfig.serverPort || 4500, function (err) {
38 | if (err) throw err
39 | console.log(`server listening on ${app.server.address().port}`)
40 | })
41 |
42 |
43 | exports = module.exports = app;
--------------------------------------------------------------------------------
/server/config/config.js:
--------------------------------------------------------------------------------
1 | const configurations = {
2 | development: {
3 | mongoUrl: 'mongodb://localhost:27017/fastify-app',
4 | seedData: false,
5 | serverPort: 4500
6 | },
7 | staging: {
8 | mongoUrl: 'mongodb://localhost:27017/fastify-app',
9 | seedData: false,
10 | serverPort: 4500
11 | },
12 | production: {
13 | mongoUrl: 'mongodb://localhost:27017/fastify-app',
14 | seedData: false,
15 | serverPort: 4500
16 | }
17 | }
18 |
19 | export const APP_ENVIRONMENTS = {
20 | DEV: 'development',
21 | PROD: 'prodoction',
22 | STAGING: 'staging'
23 | }
24 |
25 | const environment = APP_ENVIRONMENTS.DEV;
26 |
27 |
28 | export default configurations[environment];
--------------------------------------------------------------------------------
/server/db/db.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import appConfig from '../config/config';
4 | import fastifyMongodb from 'fastify-mongodb';
5 |
6 | const mongodbUrl = appConfig.mongoUrl;
7 |
8 |
9 | /**
10 | * @author Ahsan Ayaz
11 | * The function registers mongodb plugin with our fastify app
12 | * This allows to share the same mongodb throughout the whole app
13 | * @param {fastify app} app
14 | * @return Promise
15 | */
16 | const registerDB = (app) => {
17 | return new Promise((resolve, reject) => {
18 | app.register(fastifyMongodb, {
19 | url: mongodbUrl
20 | }, err => {
21 | if (err) {
22 | throw err;
23 | }else{
24 | console.log('Connected to mongo successfully');
25 | resolve();
26 | }
27 | })
28 | })
29 | }
30 |
31 |
32 | export default registerDB;
--------------------------------------------------------------------------------
/server/models/user.model.js:
--------------------------------------------------------------------------------
1 | const UserModel = {
2 | "type": "object",
3 | "properties": {
4 | "cell": {
5 | "type": "string"
6 | },
7 | "_id": {
8 | "type": "string"
9 | },
10 | "dob": {
11 | "type": "string"
12 | },
13 | "email": {
14 | "type": "string"
15 | },
16 | "gender": {
17 | "type": "string"
18 | },
19 | "location": {
20 | "properties": {
21 | "city": {
22 | "type": "string"
23 | },
24 | "postcode": {
25 | "type": "integer"
26 | },
27 | "state": {
28 | "type": "string"
29 | },
30 | "street": {
31 | "type": "string"
32 | }
33 | },
34 | "type": "object"
35 | },
36 | "login": {
37 | "properties": {
38 | "md5": {
39 | "type": "string"
40 | },
41 | "password": {
42 | "type": "string"
43 | },
44 | "salt": {
45 | "type": "string"
46 | },
47 | "sha1": {
48 | "type": "string"
49 | },
50 | "sha256": {
51 | "type": "string"
52 | },
53 | "username": {
54 | "type": "string"
55 | }
56 | },
57 | "type": "object"
58 | },
59 | "name": {
60 | "properties": {
61 | "first": {
62 | "type": "string"
63 | },
64 | "last": {
65 | "type": "string"
66 | },
67 | "title": {
68 | "type": "string"
69 | }
70 | },
71 | "type": "object"
72 | },
73 | "nat": {
74 | "type": "string"
75 | },
76 | "phone": {
77 | "type": "string"
78 | },
79 | "picture": {
80 | "properties": {
81 | "large": {
82 | "type": "string"
83 | },
84 | "medium": {
85 | "type": "string"
86 | },
87 | "thumbnail": {
88 | "type": "string"
89 | }
90 | },
91 | "type": "object"
92 | },
93 | "registered": {
94 | "type": "string"
95 | }
96 | }
97 | }
98 | export default UserModel;
--------------------------------------------------------------------------------
/server/seed/seed.data.js:
--------------------------------------------------------------------------------
1 | const seedData = [
2 | {
3 | "gender":"female",
4 | "name":{
5 | "title":"miss",
6 | "first":"chloe",
7 | "last":"bergeron"
8 | },
9 | "location":{
10 | "street":"8344 college ave",
11 | "city":"enterprise",
12 | "state":"ontario",
13 | "postcode":93109
14 | },
15 | "email":"chloe.bergeron@example.com",
16 | "login":{
17 | "username":"lazykoala851",
18 | "password":"jaybird",
19 | "salt":"MIe3JDiI",
20 | "md5":"9882aa5abe1ae75501d3d0ae841c29dc",
21 | "sha1":"3d81ba64de110a0c4847d49b7da5755443fa4a23",
22 | "sha256":"a4057f2e7e0f1b8cf690271c89049433f10ea2f2274c28b4f9b09cf142ef1a14"
23 | },
24 | "dob":"1968-04-27 13:17:42",
25 | "registered":"2010-12-04 12:39:02",
26 | "phone":"884-178-8875",
27 | "cell":"205-434-4840",
28 | "id":{
29 | "name":"",
30 | "value":null
31 | },
32 | "picture":{
33 | "large":"https://randomuser.me/api/portraits/women/14.jpg",
34 | "medium":"https://randomuser.me/api/portraits/med/women/14.jpg",
35 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/14.jpg"
36 | },
37 | "nat":"CA"
38 | },
39 | {
40 | "gender":"male",
41 | "name":{
42 | "title":"mr",
43 | "first":"adam",
44 | "last":"andersen"
45 | },
46 | "location":{
47 | "street":"5928 peel st",
48 | "city":"campbellton",
49 | "state":"nunavut",
50 | "postcode":96643
51 | },
52 | "email":"adam.andersen@example.com",
53 | "login":{
54 | "username":"smallbutterfly440",
55 | "password":"huskers",
56 | "salt":"yv55f6VJ",
57 | "md5":"a4bdea053add3a89468a022781cb23bc",
58 | "sha1":"b2be358fb1acac94ac2dd8b8cbefb4058a0c2446",
59 | "sha256":"9d1257de829e3bdcbd66e8b32b2f07b6daa0f3e9625107db585e4fc5233ddeef"
60 | },
61 | "dob":"1965-02-16 18:28:54",
62 | "registered":"2015-06-18 11:18:51",
63 | "phone":"671-357-0344",
64 | "cell":"242-303-7850",
65 | "id":{
66 | "name":"",
67 | "value":null
68 | },
69 | "picture":{
70 | "large":"https://randomuser.me/api/portraits/men/8.jpg",
71 | "medium":"https://randomuser.me/api/portraits/med/men/8.jpg",
72 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/8.jpg"
73 | },
74 | "nat":"CA"
75 | },
76 | {
77 | "gender":"male",
78 | "name":{
79 | "title":"mr",
80 | "first":"luke",
81 | "last":"brewer"
82 | },
83 | "location":{
84 | "street":"2834 park lane",
85 | "city":"athlone",
86 | "state":"kerry",
87 | "postcode":51830
88 | },
89 | "email":"luke.brewer@example.com",
90 | "login":{
91 | "username":"organicbird871",
92 | "password":"another",
93 | "salt":"31nw1hfr",
94 | "md5":"2c6766e075921f0bb2ea4dc7c38fbead",
95 | "sha1":"3ee518bc5c1aa4e8850c2d9ceb2dd698bba43879",
96 | "sha256":"db26029386a1fec80d0174055e18bdc2b86056d4b21fa212ecf466329eb6b664"
97 | },
98 | "dob":"1947-06-27 07:35:19",
99 | "registered":"2010-10-17 16:39:15",
100 | "phone":"011-238-4450",
101 | "cell":"081-834-7928",
102 | "id":{
103 | "name":"PPS",
104 | "value":"2220834T"
105 | },
106 | "picture":{
107 | "large":"https://randomuser.me/api/portraits/men/77.jpg",
108 | "medium":"https://randomuser.me/api/portraits/med/men/77.jpg",
109 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/77.jpg"
110 | },
111 | "nat":"IE"
112 | },
113 | {
114 | "gender":"male",
115 | "name":{
116 | "title":"mr",
117 | "first":"juan",
118 | "last":"arias"
119 | },
120 | "location":{
121 | "street":"6009 avenida de américa",
122 | "city":"valladolid",
123 | "state":"la rioja",
124 | "postcode":16243
125 | },
126 | "email":"juan.arias@example.com",
127 | "login":{
128 | "username":"organicduck193",
129 | "password":"fordf150",
130 | "salt":"rXDrNaLV",
131 | "md5":"b62b4dbdbe68b6c7074da3a9e24a9544",
132 | "sha1":"d64b97e9e430e0d26180506fcca9f3adc61c370d",
133 | "sha256":"b5fea4974f157d4337ab8804ec3151d35ea3eabf5a8e8959818886a4a911cbf9"
134 | },
135 | "dob":"1992-10-28 21:11:18",
136 | "registered":"2014-08-06 11:42:49",
137 | "phone":"935-860-773",
138 | "cell":"661-018-644",
139 | "id":{
140 | "name":"DNI",
141 | "value":"49559417-J"
142 | },
143 | "picture":{
144 | "large":"https://randomuser.me/api/portraits/men/28.jpg",
145 | "medium":"https://randomuser.me/api/portraits/med/men/28.jpg",
146 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/28.jpg"
147 | },
148 | "nat":"ES"
149 | },
150 | {
151 | "gender":"female",
152 | "name":{
153 | "title":"mrs",
154 | "first":"nalan",
155 | "last":"demirel"
156 | },
157 | "location":{
158 | "street":"2725 anafartalar cd",
159 | "city":"niğde",
160 | "state":"denizli",
161 | "postcode":61500
162 | },
163 | "email":"nalan.demirel@example.com",
164 | "login":{
165 | "username":"orangeduck275",
166 | "password":"passwor",
167 | "salt":"zMpJgGgm",
168 | "md5":"7d970824411da0190ad45db4fa987970",
169 | "sha1":"e556b9dd445898e433dc93368546d84c1401721e",
170 | "sha256":"27136f1947460301e6af77e20b495f4888b8393502f0ef9680198163640a8616"
171 | },
172 | "dob":"1947-07-14 12:49:06",
173 | "registered":"2012-12-26 05:40:59",
174 | "phone":"(582)-051-7271",
175 | "cell":"(286)-068-5174",
176 | "id":{
177 | "name":"",
178 | "value":null
179 | },
180 | "picture":{
181 | "large":"https://randomuser.me/api/portraits/women/56.jpg",
182 | "medium":"https://randomuser.me/api/portraits/med/women/56.jpg",
183 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/56.jpg"
184 | },
185 | "nat":"TR"
186 | },
187 | {
188 | "gender":"male",
189 | "name":{
190 | "title":"mr",
191 | "first":"naël",
192 | "last":"lecomte"
193 | },
194 | "location":{
195 | "street":"3551 avenue vauban",
196 | "city":"montreuil",
197 | "state":"gironde",
198 | "postcode":94612
199 | },
200 | "email":"naël.lecomte@example.com",
201 | "login":{
202 | "username":"purplefish294",
203 | "password":"lips",
204 | "salt":"5MTVw94R",
205 | "md5":"2d055858d698326498d2f289f28dd62d",
206 | "sha1":"2cb83e7f00220306821ac025462162670b5f03dc",
207 | "sha256":"aaa528de86e95aa8938543f9496f17402577e618efae0327697e46bf53e1d35b"
208 | },
209 | "dob":"1956-12-12 15:12:23",
210 | "registered":"2010-08-14 11:54:03",
211 | "phone":"05-77-39-49-34",
212 | "cell":"06-46-42-41-81",
213 | "id":{
214 | "name":"INSEE",
215 | "value":"1561117667588 81"
216 | },
217 | "picture":{
218 | "large":"https://randomuser.me/api/portraits/men/13.jpg",
219 | "medium":"https://randomuser.me/api/portraits/med/men/13.jpg",
220 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/13.jpg"
221 | },
222 | "nat":"FR"
223 | },
224 | {
225 | "gender":"female",
226 | "name":{
227 | "title":"ms",
228 | "first":"margarita",
229 | "last":"moreno"
230 | },
231 | "location":{
232 | "street":"6491 calle de ángel garcía",
233 | "city":"vigo",
234 | "state":"la rioja",
235 | "postcode":20304
236 | },
237 | "email":"margarita.moreno@example.com",
238 | "login":{
239 | "username":"lazyrabbit956",
240 | "password":"25802580",
241 | "salt":"w9brJRkO",
242 | "md5":"3af22c2676e3d63c26d802f4ad4ed714",
243 | "sha1":"8cf2b148ab1a0b8956fca2bb860319b212b7bf23",
244 | "sha256":"ca5bf91527af221739e5d177bacbac25c4ec1493c8f86eddcba85f9692dbf340"
245 | },
246 | "dob":"1949-05-31 22:04:31",
247 | "registered":"2006-07-17 15:39:49",
248 | "phone":"977-674-612",
249 | "cell":"694-791-978",
250 | "id":{
251 | "name":"DNI",
252 | "value":"90433638-T"
253 | },
254 | "picture":{
255 | "large":"https://randomuser.me/api/portraits/women/14.jpg",
256 | "medium":"https://randomuser.me/api/portraits/med/women/14.jpg",
257 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/14.jpg"
258 | },
259 | "nat":"ES"
260 | },
261 | {
262 | "gender":"male",
263 | "name":{
264 | "title":"mr",
265 | "first":"silas",
266 | "last":"kristensen"
267 | },
268 | "location":{
269 | "street":"2135 saturnvej",
270 | "city":"ansager",
271 | "state":"hovedstaden",
272 | "postcode":58804
273 | },
274 | "email":"silas.kristensen@example.com",
275 | "login":{
276 | "username":"greenelephant967",
277 | "password":"celica",
278 | "salt":"IpxcAfbj",
279 | "md5":"15b100a5b1d298f7ceef533f637d1e17",
280 | "sha1":"1127397edd52a8c8024036c77519109fcc7ca6d3",
281 | "sha256":"589fb403b94c5b6e50b2ba78d37fb904fc8469c55379e4444b9263d770df1ebb"
282 | },
283 | "dob":"1957-03-07 13:51:39",
284 | "registered":"2011-01-03 12:42:57",
285 | "phone":"04235176",
286 | "cell":"67911039",
287 | "id":{
288 | "name":"CPR",
289 | "value":"471033-8764"
290 | },
291 | "picture":{
292 | "large":"https://randomuser.me/api/portraits/men/15.jpg",
293 | "medium":"https://randomuser.me/api/portraits/med/men/15.jpg",
294 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/15.jpg"
295 | },
296 | "nat":"DK"
297 | },
298 | {
299 | "gender":"female",
300 | "name":{
301 | "title":"miss",
302 | "first":"emily",
303 | "last":"ruiz"
304 | },
305 | "location":{
306 | "street":"7014 chester road",
307 | "city":"bristol",
308 | "state":"grampian",
309 | "postcode":"X3A 6PF"
310 | },
311 | "email":"emily.ruiz@example.com",
312 | "login":{
313 | "username":"lazyfrog854",
314 | "password":"asia",
315 | "salt":"Y8vuhTMR",
316 | "md5":"9b6b8dada602fdb4c7c0a929ed21d9d7",
317 | "sha1":"ded722eb76b9328c8a75164465de509538fdca53",
318 | "sha256":"851f6fe802a4b12eee9f94c095a72fc3107f4c9567b951f0d255e741cbc6487c"
319 | },
320 | "dob":"1961-09-12 00:13:24",
321 | "registered":"2014-03-22 09:57:56",
322 | "phone":"019467 72371",
323 | "cell":"0711-832-949",
324 | "id":{
325 | "name":"NINO",
326 | "value":"RW 44 98 02 S"
327 | },
328 | "picture":{
329 | "large":"https://randomuser.me/api/portraits/women/73.jpg",
330 | "medium":"https://randomuser.me/api/portraits/med/women/73.jpg",
331 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/73.jpg"
332 | },
333 | "nat":"GB"
334 | },
335 | {
336 | "gender":"male",
337 | "name":{
338 | "title":"mr",
339 | "first":"علی",
340 | "last":"محمدخان"
341 | },
342 | "location":{
343 | "street":"4043 میدان ولیعصر (عج)",
344 | "city":"کرمانشاه",
345 | "state":"ایلام",
346 | "postcode":52233
347 | },
348 | "email":"علی.محمدخان@example.com",
349 | "login":{
350 | "username":"greenelephant143",
351 | "password":"icecream",
352 | "salt":"qebN6uDY",
353 | "md5":"4af8b4b336fe064b97e21216dcf37d5e",
354 | "sha1":"b2eee9c95fa6a7676b43c1ecc07865d75fa84499",
355 | "sha256":"b6677f4c3937abea9dbf02b808955dc83b5a520fd45ac7ab68dff0e5ad502dff"
356 | },
357 | "dob":"1978-09-05 06:22:29",
358 | "registered":"2004-09-05 02:25:17",
359 | "phone":"033-03364942",
360 | "cell":"0962-072-8439",
361 | "id":{
362 | "name":"",
363 | "value":null
364 | },
365 | "picture":{
366 | "large":"https://randomuser.me/api/portraits/men/27.jpg",
367 | "medium":"https://randomuser.me/api/portraits/med/men/27.jpg",
368 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/27.jpg"
369 | },
370 | "nat":"IR"
371 | },
372 | {
373 | "gender":"female",
374 | "name":{
375 | "title":"miss",
376 | "first":"emilie",
377 | "last":"chu"
378 | },
379 | "location":{
380 | "street":"7867 dieppe ave",
381 | "city":"chesterville",
382 | "state":"yukon",
383 | "postcode":27325
384 | },
385 | "email":"emilie.chu@example.com",
386 | "login":{
387 | "username":"smallpanda868",
388 | "password":"jetta",
389 | "salt":"LRTNcOMn",
390 | "md5":"444164c78612c602475cea681f18ab62",
391 | "sha1":"9971a45f39b6a1073f125f26718273b1f812490e",
392 | "sha256":"b9627a1d4d78d5a266901e6db5ed42b5477c3744747f6937c7454f90fbfc2cc4"
393 | },
394 | "dob":"1962-08-12 18:33:05",
395 | "registered":"2007-05-07 17:28:11",
396 | "phone":"094-239-1123",
397 | "cell":"904-101-7493",
398 | "id":{
399 | "name":"",
400 | "value":null
401 | },
402 | "picture":{
403 | "large":"https://randomuser.me/api/portraits/women/37.jpg",
404 | "medium":"https://randomuser.me/api/portraits/med/women/37.jpg",
405 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/37.jpg"
406 | },
407 | "nat":"CA"
408 | },
409 | {
410 | "gender":"female",
411 | "name":{
412 | "title":"miss",
413 | "first":"estelle",
414 | "last":"garnier"
415 | },
416 | "location":{
417 | "street":"2673 boulevard de la duchère",
418 | "city":"besançon",
419 | "state":"haut-rhin",
420 | "postcode":71988
421 | },
422 | "email":"estelle.garnier@example.com",
423 | "login":{
424 | "username":"organicbear191",
425 | "password":"conner",
426 | "salt":"UFFvOc1r",
427 | "md5":"14dd6ab33c20763545bef81b078dc90f",
428 | "sha1":"cf69bf5fbb051c7bcaf0c7e959e8e6a19d9890ea",
429 | "sha256":"62da260c9369172dfad99616b830f3c75c317518e06f5c21d801a1e0c45c069e"
430 | },
431 | "dob":"1954-04-17 20:45:40",
432 | "registered":"2009-02-17 19:41:02",
433 | "phone":"03-53-04-07-00",
434 | "cell":"06-29-34-34-75",
435 | "id":{
436 | "name":"INSEE",
437 | "value":"254373205213 77"
438 | },
439 | "picture":{
440 | "large":"https://randomuser.me/api/portraits/women/58.jpg",
441 | "medium":"https://randomuser.me/api/portraits/med/women/58.jpg",
442 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/58.jpg"
443 | },
444 | "nat":"FR"
445 | },
446 | {
447 | "gender":"female",
448 | "name":{
449 | "title":"mrs",
450 | "first":"zoey",
451 | "last":"fisher"
452 | },
453 | "location":{
454 | "street":"6075 hogan st",
455 | "city":"shepparton",
456 | "state":"tasmania",
457 | "postcode":1780
458 | },
459 | "email":"zoey.fisher@example.com",
460 | "login":{
461 | "username":"beautifulswan693",
462 | "password":"slim",
463 | "salt":"P8wyy76Q",
464 | "md5":"2070ca25539a2335274502d7e1061bfe",
465 | "sha1":"328c4887f921c853cf19906edcbb430efa4ebf9d",
466 | "sha256":"b88c8910ee517bd28feab125f00fbcd53dcd29e8e62615237a94eb3a779bd52a"
467 | },
468 | "dob":"1967-04-16 23:52:01",
469 | "registered":"2004-04-04 16:43:32",
470 | "phone":"03-9548-8288",
471 | "cell":"0443-028-750",
472 | "id":{
473 | "name":"TFN",
474 | "value":"860887480"
475 | },
476 | "picture":{
477 | "large":"https://randomuser.me/api/portraits/women/28.jpg",
478 | "medium":"https://randomuser.me/api/portraits/med/women/28.jpg",
479 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/28.jpg"
480 | },
481 | "nat":"AU"
482 | },
483 | {
484 | "gender":"female",
485 | "name":{
486 | "title":"miss",
487 | "first":"maeva",
488 | "last":"young"
489 | },
490 | "location":{
491 | "street":"6240 20th ave",
492 | "city":"vanier",
493 | "state":"québec",
494 | "postcode":93358
495 | },
496 | "email":"maeva.young@example.com",
497 | "login":{
498 | "username":"bluesnake300",
499 | "password":"kikimora",
500 | "salt":"jm3U1U2t",
501 | "md5":"eafb979ed8550e0b9f124755f42eadcc",
502 | "sha1":"151ea751c3e8ea720c13235f07eb84d9842e57c4",
503 | "sha256":"624d5b657c67a20e23bb289706f52cb419fef795dd96ac2f627aa78ec4977c3f"
504 | },
505 | "dob":"1989-01-21 23:36:58",
506 | "registered":"2015-08-28 23:37:04",
507 | "phone":"447-766-5197",
508 | "cell":"300-293-1061",
509 | "id":{
510 | "name":"",
511 | "value":null
512 | },
513 | "picture":{
514 | "large":"https://randomuser.me/api/portraits/women/32.jpg",
515 | "medium":"https://randomuser.me/api/portraits/med/women/32.jpg",
516 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/32.jpg"
517 | },
518 | "nat":"CA"
519 | },
520 | {
521 | "gender":"female",
522 | "name":{
523 | "title":"mrs",
524 | "first":"eléonore",
525 | "last":"renaud"
526 | },
527 | "location":{
528 | "street":"8067 place de l'abbé-basset",
529 | "city":"vitry-sur-seine",
530 | "state":"côte-d'or",
531 | "postcode":57560
532 | },
533 | "email":"eléonore.renaud@example.com",
534 | "login":{
535 | "username":"goldenfish100",
536 | "password":"forever",
537 | "salt":"kJxrHTYi",
538 | "md5":"b54fb7fd4ac854578e9027af5edfc80f",
539 | "sha1":"f58c1ebf5c6a48097621b408b531e8be878bf9c7",
540 | "sha256":"5a74903e8deef38120ae86687495cabcd47cc1848a01725a782c757ace64454e"
541 | },
542 | "dob":"1944-09-26 09:21:09",
543 | "registered":"2006-12-06 15:52:36",
544 | "phone":"03-74-03-66-63",
545 | "cell":"06-37-53-91-76",
546 | "id":{
547 | "name":"INSEE",
548 | "value":"244857935733 15"
549 | },
550 | "picture":{
551 | "large":"https://randomuser.me/api/portraits/women/49.jpg",
552 | "medium":"https://randomuser.me/api/portraits/med/women/49.jpg",
553 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/49.jpg"
554 | },
555 | "nat":"FR"
556 | },
557 | {
558 | "gender":"male",
559 | "name":{
560 | "title":"mr",
561 | "first":"william",
562 | "last":"ambrose"
563 | },
564 | "location":{
565 | "street":"7599 tecumseh rd",
566 | "city":"maitland",
567 | "state":"ontario",
568 | "postcode":51758
569 | },
570 | "email":"william.ambrose@example.com",
571 | "login":{
572 | "username":"smallmeercat729",
573 | "password":"allan",
574 | "salt":"a3u3YEuq",
575 | "md5":"2133a961b82937e8cb39e7adcb712818",
576 | "sha1":"f5f388d551b15a394240979d2f510647568c4dbc",
577 | "sha256":"49e29de0faa2d8422e57cd01381cc5b618e1c3e9ede079a08b81a44bc864ef16"
578 | },
579 | "dob":"1946-09-24 19:48:44",
580 | "registered":"2007-03-18 13:47:02",
581 | "phone":"866-982-0782",
582 | "cell":"364-750-9642",
583 | "id":{
584 | "name":"",
585 | "value":null
586 | },
587 | "picture":{
588 | "large":"https://randomuser.me/api/portraits/men/27.jpg",
589 | "medium":"https://randomuser.me/api/portraits/med/men/27.jpg",
590 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/27.jpg"
591 | },
592 | "nat":"CA"
593 | },
594 | {
595 | "gender":"female",
596 | "name":{
597 | "title":"miss",
598 | "first":"sue",
599 | "last":"flores"
600 | },
601 | "location":{
602 | "street":"6553 samaritan dr",
603 | "city":"kalgoorlie",
604 | "state":"queensland",
605 | "postcode":227
606 | },
607 | "email":"sue.flores@example.com",
608 | "login":{
609 | "username":"brownfrog970",
610 | "password":"super1",
611 | "salt":"4HZgN2ca",
612 | "md5":"ffc6134a095d4251e430cef397c6d7a1",
613 | "sha1":"b7c383fb68f9ce0ef6b1adb1335ec08f14975739",
614 | "sha256":"5a65889c05c057919e0c3a92bf3a9497585b850987bf084b0bfb6fe1906f3a4c"
615 | },
616 | "dob":"1979-11-15 00:23:47",
617 | "registered":"2006-01-25 15:59:16",
618 | "phone":"06-2735-9596",
619 | "cell":"0412-694-059",
620 | "id":{
621 | "name":"TFN",
622 | "value":"484593343"
623 | },
624 | "picture":{
625 | "large":"https://randomuser.me/api/portraits/women/21.jpg",
626 | "medium":"https://randomuser.me/api/portraits/med/women/21.jpg",
627 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/21.jpg"
628 | },
629 | "nat":"AU"
630 | },
631 | {
632 | "gender":"female",
633 | "name":{
634 | "title":"mrs",
635 | "first":"zoe",
636 | "last":"wilson"
637 | },
638 | "location":{
639 | "street":"9760 arctic way",
640 | "city":"chatham",
641 | "state":"alberta",
642 | "postcode":23867
643 | },
644 | "email":"zoe.wilson@example.com",
645 | "login":{
646 | "username":"redrabbit677",
647 | "password":"hack",
648 | "salt":"Clz9g53w",
649 | "md5":"a2fdd4626fa1eae873aa577c973ae4a9",
650 | "sha1":"a67bbb0ee2629d324984867f501f211b00fb2473",
651 | "sha256":"275bafc0a8fca43e3648925aff4f921e913c047476ce91b6151d76997eb705bc"
652 | },
653 | "dob":"1971-08-06 23:01:13",
654 | "registered":"2014-07-06 17:49:39",
655 | "phone":"240-323-5266",
656 | "cell":"452-531-4732",
657 | "id":{
658 | "name":"",
659 | "value":null
660 | },
661 | "picture":{
662 | "large":"https://randomuser.me/api/portraits/women/30.jpg",
663 | "medium":"https://randomuser.me/api/portraits/med/women/30.jpg",
664 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/30.jpg"
665 | },
666 | "nat":"CA"
667 | },
668 | {
669 | "gender":"female",
670 | "name":{
671 | "title":"miss",
672 | "first":"inaya",
673 | "last":"girard"
674 | },
675 | "location":{
676 | "street":"8236 rue dubois",
677 | "city":"nantes",
678 | "state":"isère",
679 | "postcode":41724
680 | },
681 | "email":"inaya.girard@example.com",
682 | "login":{
683 | "username":"bluepeacock287",
684 | "password":"jericho",
685 | "salt":"sfpOHJkb",
686 | "md5":"80f17554ccda489f0c6e2b3427ffadf0",
687 | "sha1":"c6b637fb7790fd7c508e6028d36ee36f4b6afcd4",
688 | "sha256":"b6521270651f6ca68ada572af9025165a99b574833d4f753274fef4874c6a293"
689 | },
690 | "dob":"1960-11-28 17:10:42",
691 | "registered":"2002-12-13 19:09:19",
692 | "phone":"04-77-15-10-49",
693 | "cell":"06-10-29-50-03",
694 | "id":{
695 | "name":"INSEE",
696 | "value":"2601064460158 54"
697 | },
698 | "picture":{
699 | "large":"https://randomuser.me/api/portraits/women/1.jpg",
700 | "medium":"https://randomuser.me/api/portraits/med/women/1.jpg",
701 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/1.jpg"
702 | },
703 | "nat":"FR"
704 | },
705 | {
706 | "gender":"female",
707 | "name":{
708 | "title":"miss",
709 | "first":"مریم",
710 | "last":"کریمی"
711 | },
712 | "location":{
713 | "street":"1913 دماوند",
714 | "city":"قائمشهر",
715 | "state":"سمنان",
716 | "postcode":82066
717 | },
718 | "email":"مریم.کریمی@example.com",
719 | "login":{
720 | "username":"lazymeercat864",
721 | "password":"vancouve",
722 | "salt":"jH0C3FEH",
723 | "md5":"03afa04ff093652a4d9cbb7b9642aff8",
724 | "sha1":"3bdc016b99b4b0c5742fecb1ddea29acb0d7639a",
725 | "sha256":"bf3b591ac0e97a4b12646088dff78c2cd77e14e797f266a2d0d3194ad18126ab"
726 | },
727 | "dob":"1982-01-03 06:27:41",
728 | "registered":"2013-08-10 14:37:32",
729 | "phone":"077-54963401",
730 | "cell":"0995-309-9535",
731 | "id":{
732 | "name":"",
733 | "value":null
734 | },
735 | "picture":{
736 | "large":"https://randomuser.me/api/portraits/women/18.jpg",
737 | "medium":"https://randomuser.me/api/portraits/med/women/18.jpg",
738 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/18.jpg"
739 | },
740 | "nat":"IR"
741 | },
742 | {
743 | "gender":"female",
744 | "name":{
745 | "title":"miss",
746 | "first":"joyce",
747 | "last":"hart"
748 | },
749 | "location":{
750 | "street":"1232 homestead rd",
751 | "city":"athens",
752 | "state":"oregon",
753 | "postcode":89840
754 | },
755 | "email":"joyce.hart@example.com",
756 | "login":{
757 | "username":"organicgorilla172",
758 | "password":"dancing",
759 | "salt":"sqZ72exx",
760 | "md5":"97b37ad0e1181d7f8f7a5a51ee18d5e6",
761 | "sha1":"742cba67d62b9b0a7c6be8d1b66d97758bc60d03",
762 | "sha256":"b1d0b73f91cafc6894289dae8c592815330a166a05bda1457461c10b3f32f367"
763 | },
764 | "dob":"1987-05-20 03:02:45",
765 | "registered":"2013-03-24 03:04:39",
766 | "phone":"(243)-781-4886",
767 | "cell":"(888)-797-2826",
768 | "id":{
769 | "name":"SSN",
770 | "value":"520-58-8438"
771 | },
772 | "picture":{
773 | "large":"https://randomuser.me/api/portraits/women/15.jpg",
774 | "medium":"https://randomuser.me/api/portraits/med/women/15.jpg",
775 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/15.jpg"
776 | },
777 | "nat":"US"
778 | },
779 | {
780 | "gender":"female",
781 | "name":{
782 | "title":"miss",
783 | "first":"phoebe",
784 | "last":"chen"
785 | },
786 | "location":{
787 | "street":"6464 victoria street",
788 | "city":"wellington",
789 | "state":"west coast",
790 | "postcode":16383
791 | },
792 | "email":"phoebe.chen@example.com",
793 | "login":{
794 | "username":"organicdog124",
795 | "password":"10101010",
796 | "salt":"m1KEy4h7",
797 | "md5":"c150dcde0298704a6dcbc32e1860fdc8",
798 | "sha1":"cc1b5bc39811f98aaa0ed560c7d588608cb40456",
799 | "sha256":"697bcf25f7f539df807d38f8eb8c087655dee99c284cf480160877b67baa8a72"
800 | },
801 | "dob":"1983-06-09 13:08:17",
802 | "registered":"2005-05-07 14:40:23",
803 | "phone":"(830)-622-7639",
804 | "cell":"(076)-726-7724",
805 | "id":{
806 | "name":"",
807 | "value":null
808 | },
809 | "picture":{
810 | "large":"https://randomuser.me/api/portraits/women/76.jpg",
811 | "medium":"https://randomuser.me/api/portraits/med/women/76.jpg",
812 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/76.jpg"
813 | },
814 | "nat":"NZ"
815 | },
816 | {
817 | "gender":"male",
818 | "name":{
819 | "title":"mr",
820 | "first":"frederikke",
821 | "last":"johansen"
822 | },
823 | "location":{
824 | "street":"4831 grantofteparken",
825 | "city":"oure",
826 | "state":"syddanmark",
827 | "postcode":63617
828 | },
829 | "email":"frederikke.johansen@example.com",
830 | "login":{
831 | "username":"goldenmeercat971",
832 | "password":"lillian",
833 | "salt":"3oIckrSB",
834 | "md5":"6c3266fd8fb7c92b3454b3dfb1116e71",
835 | "sha1":"a72261528a29bd3530cbf0b39fd6d82b18329025",
836 | "sha256":"5cbea62050b3d0c6c49602180cbaf1faa87daaa7f2fa36b7dce045435478c632"
837 | },
838 | "dob":"1966-05-26 03:31:42",
839 | "registered":"2008-02-22 20:54:43",
840 | "phone":"02889011",
841 | "cell":"05771068",
842 | "id":{
843 | "name":"CPR",
844 | "value":"568947-9947"
845 | },
846 | "picture":{
847 | "large":"https://randomuser.me/api/portraits/men/14.jpg",
848 | "medium":"https://randomuser.me/api/portraits/med/men/14.jpg",
849 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/14.jpg"
850 | },
851 | "nat":"DK"
852 | },
853 | {
854 | "gender":"male",
855 | "name":{
856 | "title":"mr",
857 | "first":"carl",
858 | "last":"olsen"
859 | },
860 | "location":{
861 | "street":"7074 skovbyvej",
862 | "city":"viby sj.",
863 | "state":"sjælland",
864 | "postcode":99825
865 | },
866 | "email":"carl.olsen@example.com",
867 | "login":{
868 | "username":"ticklishleopard800",
869 | "password":"harley1",
870 | "salt":"N5uL7DZW",
871 | "md5":"4a0fb54fefd45c125435bd4e12924d6f",
872 | "sha1":"0a0ea0bb30bc9943794f2078b67fd9ce20d3e2b4",
873 | "sha256":"b1f28612f31e0beb82fa529f36fd742075e0472b9dbc7fdfd6140f98455947eb"
874 | },
875 | "dob":"1974-08-28 19:36:51",
876 | "registered":"2015-08-27 06:33:26",
877 | "phone":"51083001",
878 | "cell":"36953854",
879 | "id":{
880 | "name":"CPR",
881 | "value":"588643-4100"
882 | },
883 | "picture":{
884 | "large":"https://randomuser.me/api/portraits/men/35.jpg",
885 | "medium":"https://randomuser.me/api/portraits/med/men/35.jpg",
886 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/35.jpg"
887 | },
888 | "nat":"DK"
889 | },
890 | {
891 | "gender":"male",
892 | "name":{
893 | "title":"monsieur",
894 | "first":"damien",
895 | "last":"rousseau"
896 | },
897 | "location":{
898 | "street":"2284 rue baraban",
899 | "city":"morges 1",
900 | "state":"zug",
901 | "postcode":6038
902 | },
903 | "email":"damien.rousseau@example.com",
904 | "login":{
905 | "username":"orangeleopard548",
906 | "password":"rage",
907 | "salt":"CBrQZex4",
908 | "md5":"9de803a7f5fa118ed9a1fb4bf3a15e1a",
909 | "sha1":"9e164e7566fc377478044d88528a465b39c802f2",
910 | "sha256":"5cc52b8c6fc2ef799350a1e4e24fc67f06d3c4077e7959ffb222c5d8da1d8e1d"
911 | },
912 | "dob":"1986-12-16 11:07:41",
913 | "registered":"2012-11-19 03:47:18",
914 | "phone":"(779)-936-3133",
915 | "cell":"(848)-891-8749",
916 | "id":{
917 | "name":"AVS",
918 | "value":"756.KQJW.HSMG.39"
919 | },
920 | "picture":{
921 | "large":"https://randomuser.me/api/portraits/men/32.jpg",
922 | "medium":"https://randomuser.me/api/portraits/med/men/32.jpg",
923 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/32.jpg"
924 | },
925 | "nat":"CH"
926 | },
927 | {
928 | "gender":"female",
929 | "name":{
930 | "title":"miss",
931 | "first":"misty",
932 | "last":"bell"
933 | },
934 | "location":{
935 | "street":"6070 hickory creek dr",
936 | "city":"darwin",
937 | "state":"australian capital territory",
938 | "postcode":1634
939 | },
940 | "email":"misty.bell@example.com",
941 | "login":{
942 | "username":"bigduck978",
943 | "password":"sf49ers",
944 | "salt":"rZ1UZiQ6",
945 | "md5":"10f044b6b2fb789c54d37609c9b89112",
946 | "sha1":"1a495c1b07337426f1e26900beac83bcbd9f2e57",
947 | "sha256":"a7d3c6b6483b834f7dc2b6ab01a7831da0f31e0650a6b3f3e1b9512271fc7478"
948 | },
949 | "dob":"1992-03-12 10:21:34",
950 | "registered":"2010-12-02 16:58:01",
951 | "phone":"09-0080-1984",
952 | "cell":"0422-344-352",
953 | "id":{
954 | "name":"TFN",
955 | "value":"450431926"
956 | },
957 | "picture":{
958 | "large":"https://randomuser.me/api/portraits/women/39.jpg",
959 | "medium":"https://randomuser.me/api/portraits/med/women/39.jpg",
960 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/39.jpg"
961 | },
962 | "nat":"AU"
963 | },
964 | {
965 | "gender":"male",
966 | "name":{
967 | "title":"mr",
968 | "first":"james",
969 | "last":"harris"
970 | },
971 | "location":{
972 | "street":"8997 anglesea street",
973 | "city":"greymouth",
974 | "state":"tasman",
975 | "postcode":79963
976 | },
977 | "email":"james.harris@example.com",
978 | "login":{
979 | "username":"beautifulfish891",
980 | "password":"pimping",
981 | "salt":"IfSYl7Xn",
982 | "md5":"548b1eb89335e9b5f3ff48384280dbfa",
983 | "sha1":"d10918b7dceac87427e14e4b30b019657e837907",
984 | "sha256":"686557ab8895853546e0b6996e9f4f1c039e2916917c3e6dd38e0e343d01369c"
985 | },
986 | "dob":"1951-01-22 23:07:13",
987 | "registered":"2014-03-16 12:44:20",
988 | "phone":"(261)-699-1512",
989 | "cell":"(471)-720-5163",
990 | "id":{
991 | "name":"",
992 | "value":null
993 | },
994 | "picture":{
995 | "large":"https://randomuser.me/api/portraits/men/69.jpg",
996 | "medium":"https://randomuser.me/api/portraits/med/men/69.jpg",
997 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/69.jpg"
998 | },
999 | "nat":"NZ"
1000 | },
1001 | {
1002 | "gender":"female",
1003 | "name":{
1004 | "title":"miss",
1005 | "first":"marie",
1006 | "last":"gordon"
1007 | },
1008 | "location":{
1009 | "street":"3541 oak lawn ave",
1010 | "city":"paterson",
1011 | "state":"iowa",
1012 | "postcode":65213
1013 | },
1014 | "email":"marie.gordon@example.com",
1015 | "login":{
1016 | "username":"tinyrabbit229",
1017 | "password":"414141",
1018 | "salt":"zEesSI9v",
1019 | "md5":"58543f4fd8375dce27340cb268ed0353",
1020 | "sha1":"053d7ac7b3a10fa180eab95b6789a017a5b8e4c6",
1021 | "sha256":"a415d8c95b07d15f071e8176305a57d82207fa4100870f360ccb3058234fded6"
1022 | },
1023 | "dob":"1958-10-21 03:10:21",
1024 | "registered":"2013-12-06 14:52:07",
1025 | "phone":"(154)-255-7987",
1026 | "cell":"(848)-182-7145",
1027 | "id":{
1028 | "name":"SSN",
1029 | "value":"118-21-6962"
1030 | },
1031 | "picture":{
1032 | "large":"https://randomuser.me/api/portraits/women/65.jpg",
1033 | "medium":"https://randomuser.me/api/portraits/med/women/65.jpg",
1034 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/65.jpg"
1035 | },
1036 | "nat":"US"
1037 | },
1038 | {
1039 | "gender":"female",
1040 | "name":{
1041 | "title":"miss",
1042 | "first":"hailey",
1043 | "last":"white"
1044 | },
1045 | "location":{
1046 | "street":"7320 york st",
1047 | "city":"cochrane",
1048 | "state":"alberta",
1049 | "postcode":23161
1050 | },
1051 | "email":"hailey.white@example.com",
1052 | "login":{
1053 | "username":"whiteswan602",
1054 | "password":"beavis",
1055 | "salt":"5brU25re",
1056 | "md5":"fa3a429094cad0c77c302ad762c81725",
1057 | "sha1":"aaedbae2e904e9c40ad9f09152d702bd57e866e9",
1058 | "sha256":"9e5ada92a5f06aee09474dda1e8a2b65bb2148ff04dadda5825509b2820caf25"
1059 | },
1060 | "dob":"1946-03-28 12:50:34",
1061 | "registered":"2013-07-09 16:35:07",
1062 | "phone":"316-331-3602",
1063 | "cell":"725-165-7062",
1064 | "id":{
1065 | "name":"",
1066 | "value":null
1067 | },
1068 | "picture":{
1069 | "large":"https://randomuser.me/api/portraits/women/40.jpg",
1070 | "medium":"https://randomuser.me/api/portraits/med/women/40.jpg",
1071 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/40.jpg"
1072 | },
1073 | "nat":"CA"
1074 | },
1075 | {
1076 | "gender":"female",
1077 | "name":{
1078 | "title":"madame",
1079 | "first":"capucine",
1080 | "last":"robin"
1081 | },
1082 | "location":{
1083 | "street":"8025 place du 22 novembre 1943",
1084 | "city":"colombier vd",
1085 | "state":"basel-stadt",
1086 | "postcode":7883
1087 | },
1088 | "email":"capucine.robin@example.com",
1089 | "login":{
1090 | "username":"organicleopard560",
1091 | "password":"luck",
1092 | "salt":"4DX23Ig6",
1093 | "md5":"d3136a7a060a025aef7aa050b7fb06d2",
1094 | "sha1":"5db126702a93991b23b874a616735e4d286ca3b3",
1095 | "sha256":"a06cbbef4275d58de7829758e87c7ee8e731072e2b69d46d5ba2210f2b8251bc"
1096 | },
1097 | "dob":"1949-12-14 06:00:44",
1098 | "registered":"2011-05-04 03:54:30",
1099 | "phone":"(144)-535-8418",
1100 | "cell":"(264)-756-0159",
1101 | "id":{
1102 | "name":"AVS",
1103 | "value":"756.QTLU.MTIW.20"
1104 | },
1105 | "picture":{
1106 | "large":"https://randomuser.me/api/portraits/women/32.jpg",
1107 | "medium":"https://randomuser.me/api/portraits/med/women/32.jpg",
1108 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/32.jpg"
1109 | },
1110 | "nat":"CH"
1111 | },
1112 | {
1113 | "gender":"female",
1114 | "name":{
1115 | "title":"ms",
1116 | "first":"mathilda",
1117 | "last":"metzger"
1118 | },
1119 | "location":{
1120 | "street":"2059 königsberger straße",
1121 | "city":"suhl",
1122 | "state":"mecklenburg-vorpommern",
1123 | "postcode":34544
1124 | },
1125 | "email":"mathilda.metzger@example.com",
1126 | "login":{
1127 | "username":"bigladybug541",
1128 | "password":"iguana",
1129 | "salt":"H0vtSdKR",
1130 | "md5":"77b2e9e545f6aed28a6438797a6eefe9",
1131 | "sha1":"7bec51f5315cae420053131c0540b55a31d06c41",
1132 | "sha256":"a2070a9f18880c2f48bf3f5c25709da8f5b2a9326fa06b5e891175aee593b258"
1133 | },
1134 | "dob":"1976-05-01 03:20:51",
1135 | "registered":"2013-06-24 21:00:20",
1136 | "phone":"0028-0603124",
1137 | "cell":"0173-6399730",
1138 | "id":{
1139 | "name":"",
1140 | "value":null
1141 | },
1142 | "picture":{
1143 | "large":"https://randomuser.me/api/portraits/women/39.jpg",
1144 | "medium":"https://randomuser.me/api/portraits/med/women/39.jpg",
1145 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/39.jpg"
1146 | },
1147 | "nat":"DE"
1148 | },
1149 | {
1150 | "gender":"female",
1151 | "name":{
1152 | "title":"ms",
1153 | "first":"georgia",
1154 | "last":"wheeler"
1155 | },
1156 | "location":{
1157 | "street":"8848 e sandy lake rd",
1158 | "city":"darwin",
1159 | "state":"new south wales",
1160 | "postcode":6040
1161 | },
1162 | "email":"georgia.wheeler@example.com",
1163 | "login":{
1164 | "username":"brownfrog912",
1165 | "password":"gilbert",
1166 | "salt":"MsS6HTK9",
1167 | "md5":"c5fd26c1cac6f53a1b18d4f8c144be46",
1168 | "sha1":"8bb577a0d4bc4f27ff27f7b28115ea2f5ca3eed4",
1169 | "sha256":"c148db1fa1aef99afbb2c2774d6ee40c771c3de059b6dc14a7b4141cdb262cde"
1170 | },
1171 | "dob":"1968-05-05 15:36:56",
1172 | "registered":"2007-07-27 05:05:44",
1173 | "phone":"07-0809-4237",
1174 | "cell":"0439-237-744",
1175 | "id":{
1176 | "name":"TFN",
1177 | "value":"412631876"
1178 | },
1179 | "picture":{
1180 | "large":"https://randomuser.me/api/portraits/women/4.jpg",
1181 | "medium":"https://randomuser.me/api/portraits/med/women/4.jpg",
1182 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/4.jpg"
1183 | },
1184 | "nat":"AU"
1185 | },
1186 | {
1187 | "gender":"female",
1188 | "name":{
1189 | "title":"ms",
1190 | "first":"chloe",
1191 | "last":"davis"
1192 | },
1193 | "location":{
1194 | "street":"7184 herbert road",
1195 | "city":"ashbourne",
1196 | "state":"kildare",
1197 | "postcode":21109
1198 | },
1199 | "email":"chloe.davis@example.com",
1200 | "login":{
1201 | "username":"blueostrich956",
1202 | "password":"hover",
1203 | "salt":"nowDOn2t",
1204 | "md5":"c7e24ea2552c7d4d0419f5c3628edb02",
1205 | "sha1":"dfff1da7aa1b0fd190423e658f91100dc44d040e",
1206 | "sha256":"5e33e607434429ff6bebf2f9739d18769ad8bcd8e6f9cdb4e65ad32bffd4779a"
1207 | },
1208 | "dob":"1947-01-09 19:28:18",
1209 | "registered":"2005-07-04 21:57:14",
1210 | "phone":"021-603-6610",
1211 | "cell":"081-355-8323",
1212 | "id":{
1213 | "name":"PPS",
1214 | "value":"8979146T"
1215 | },
1216 | "picture":{
1217 | "large":"https://randomuser.me/api/portraits/women/14.jpg",
1218 | "medium":"https://randomuser.me/api/portraits/med/women/14.jpg",
1219 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/14.jpg"
1220 | },
1221 | "nat":"IE"
1222 | },
1223 | {
1224 | "gender":"female",
1225 | "name":{
1226 | "title":"mrs",
1227 | "first":"marie",
1228 | "last":"stevens"
1229 | },
1230 | "location":{
1231 | "street":"3325 main street",
1232 | "city":"carlisle",
1233 | "state":"south yorkshire",
1234 | "postcode":"FT5 0ZU"
1235 | },
1236 | "email":"marie.stevens@example.com",
1237 | "login":{
1238 | "username":"bluerabbit137",
1239 | "password":"tasha",
1240 | "salt":"hfHLzR8a",
1241 | "md5":"9f93608dd7f60c35dd742eb1f3241049",
1242 | "sha1":"68440a8e94dcf45d42f2d264c4c0feedf07cd7c6",
1243 | "sha256":"561a026efc3bd6b9b7bbfaf0426ee4c2099d46391c8858314c6dfdb96e2fe6c5"
1244 | },
1245 | "dob":"1950-11-14 10:58:50",
1246 | "registered":"2005-11-02 23:39:14",
1247 | "phone":"016973 52436",
1248 | "cell":"0716-632-369",
1249 | "id":{
1250 | "name":"NINO",
1251 | "value":"JM 75 21 83 Q"
1252 | },
1253 | "picture":{
1254 | "large":"https://randomuser.me/api/portraits/women/93.jpg",
1255 | "medium":"https://randomuser.me/api/portraits/med/women/93.jpg",
1256 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/93.jpg"
1257 | },
1258 | "nat":"GB"
1259 | },
1260 | {
1261 | "gender":"male",
1262 | "name":{
1263 | "title":"mr",
1264 | "first":"hudson",
1265 | "last":"miller"
1266 | },
1267 | "location":{
1268 | "street":"7264 regent ave",
1269 | "city":"enterprise",
1270 | "state":"saskatchewan",
1271 | "postcode":59906
1272 | },
1273 | "email":"hudson.miller@example.com",
1274 | "login":{
1275 | "username":"purplebutterfly527",
1276 | "password":"skater",
1277 | "salt":"tDNyYzNS",
1278 | "md5":"87ed3ad0265712e29e977697dc45d139",
1279 | "sha1":"f6f7c5295ce47b0b3d77019c43f70710cfcd404a",
1280 | "sha256":"580f20865172281e33f47ecc359cc282a1172da31e8ce8124ba27c35eb3a8e42"
1281 | },
1282 | "dob":"1978-07-25 10:00:30",
1283 | "registered":"2006-09-13 09:42:51",
1284 | "phone":"024-705-4856",
1285 | "cell":"121-470-5855",
1286 | "id":{
1287 | "name":"",
1288 | "value":null
1289 | },
1290 | "picture":{
1291 | "large":"https://randomuser.me/api/portraits/men/78.jpg",
1292 | "medium":"https://randomuser.me/api/portraits/med/men/78.jpg",
1293 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/78.jpg"
1294 | },
1295 | "nat":"CA"
1296 | },
1297 | {
1298 | "gender":"male",
1299 | "name":{
1300 | "title":"mr",
1301 | "first":"miguel",
1302 | "last":"reyes"
1303 | },
1304 | "location":{
1305 | "street":"2988 calle de toledo",
1306 | "city":"burgos",
1307 | "state":"galicia",
1308 | "postcode":53459
1309 | },
1310 | "email":"miguel.reyes@example.com",
1311 | "login":{
1312 | "username":"tinyleopard711",
1313 | "password":"gizmo1",
1314 | "salt":"ijueOITN",
1315 | "md5":"315273992e68cbb3b1427483376c361f",
1316 | "sha1":"9a30c4fa6d2d7d459d079c3801466b13cc793e19",
1317 | "sha256":"b7a610794c284376394cfd7e0c18f16f44c40a31271e188119c17298b8b21d66"
1318 | },
1319 | "dob":"1971-10-18 17:43:28",
1320 | "registered":"2008-07-02 00:52:55",
1321 | "phone":"990-951-814",
1322 | "cell":"684-387-502",
1323 | "id":{
1324 | "name":"DNI",
1325 | "value":"21379429-P"
1326 | },
1327 | "picture":{
1328 | "large":"https://randomuser.me/api/portraits/men/95.jpg",
1329 | "medium":"https://randomuser.me/api/portraits/med/men/95.jpg",
1330 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/95.jpg"
1331 | },
1332 | "nat":"ES"
1333 | },
1334 | {
1335 | "gender":"female",
1336 | "name":{
1337 | "title":"mrs",
1338 | "first":"sophia",
1339 | "last":"long"
1340 | },
1341 | "location":{
1342 | "street":"7627 ash dr",
1343 | "city":"australian capital territory",
1344 | "state":"new south wales",
1345 | "postcode":319
1346 | },
1347 | "email":"sophia.long@example.com",
1348 | "login":{
1349 | "username":"tinybird884",
1350 | "password":"rugby",
1351 | "salt":"62MLjtCP",
1352 | "md5":"d48b1eb07234d34565dc198905afce8c",
1353 | "sha1":"803a08cf5d55c06b2ac1aa388bd57ef59939a3b0",
1354 | "sha256":"f28d02c455817ee5b0c44681ea7d1332dfa27cd8c83c2bffa55657d75f91df4b"
1355 | },
1356 | "dob":"1954-05-08 10:21:23",
1357 | "registered":"2009-01-26 15:27:36",
1358 | "phone":"07-4921-9146",
1359 | "cell":"0435-146-126",
1360 | "id":{
1361 | "name":"TFN",
1362 | "value":"221302712"
1363 | },
1364 | "picture":{
1365 | "large":"https://randomuser.me/api/portraits/women/34.jpg",
1366 | "medium":"https://randomuser.me/api/portraits/med/women/34.jpg",
1367 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/34.jpg"
1368 | },
1369 | "nat":"AU"
1370 | },
1371 | {
1372 | "gender":"male",
1373 | "name":{
1374 | "title":"mr",
1375 | "first":"joaquin",
1376 | "last":"fernandez"
1377 | },
1378 | "location":{
1379 | "street":"6592 ronda de toledo",
1380 | "city":"hospitalet de llobregat",
1381 | "state":"ceuta",
1382 | "postcode":54666
1383 | },
1384 | "email":"joaquin.fernandez@example.com",
1385 | "login":{
1386 | "username":"silverladybug723",
1387 | "password":"thomas1",
1388 | "salt":"ZEFHU8P4",
1389 | "md5":"d7837e0fded6b0828d7e46ab9050b5ad",
1390 | "sha1":"d9a02aace7a8a0a4fd2086036e6226a7d6db9d53",
1391 | "sha256":"560e38c8959e34ff97ae5ef2a249648eea59b89896114a361803a42881857074"
1392 | },
1393 | "dob":"1977-01-08 07:12:17",
1394 | "registered":"2010-10-21 22:43:05",
1395 | "phone":"920-256-337",
1396 | "cell":"605-281-879",
1397 | "id":{
1398 | "name":"DNI",
1399 | "value":"20867378-N"
1400 | },
1401 | "picture":{
1402 | "large":"https://randomuser.me/api/portraits/men/87.jpg",
1403 | "medium":"https://randomuser.me/api/portraits/med/men/87.jpg",
1404 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/87.jpg"
1405 | },
1406 | "nat":"ES"
1407 | },
1408 | {
1409 | "gender":"male",
1410 | "name":{
1411 | "title":"mr",
1412 | "first":"abraham",
1413 | "last":"coremans"
1414 | },
1415 | "location":{
1416 | "street":"3986 minrebroederstraat",
1417 | "city":"lochem",
1418 | "state":"limburg",
1419 | "postcode":76821
1420 | },
1421 | "email":"abraham.coremans@example.com",
1422 | "login":{
1423 | "username":"smallostrich922",
1424 | "password":"456654",
1425 | "salt":"zAWCYCAr",
1426 | "md5":"005fcc956435697f104c0988b228e9a9",
1427 | "sha1":"5125bea3894bec3e3c8d9472892836a3bb0126d7",
1428 | "sha256":"02ecd410ed0524a304dc64468e10927e06e9fe2918b8628245fb926e294c51d8"
1429 | },
1430 | "dob":"1968-10-12 21:36:51",
1431 | "registered":"2016-07-11 05:11:57",
1432 | "phone":"(762)-235-2767",
1433 | "cell":"(264)-333-4953",
1434 | "id":{
1435 | "name":"BSN",
1436 | "value":"67748916"
1437 | },
1438 | "picture":{
1439 | "large":"https://randomuser.me/api/portraits/men/45.jpg",
1440 | "medium":"https://randomuser.me/api/portraits/med/men/45.jpg",
1441 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/45.jpg"
1442 | },
1443 | "nat":"NL"
1444 | },
1445 | {
1446 | "gender":"female",
1447 | "name":{
1448 | "title":"ms",
1449 | "first":"تینا",
1450 | "last":"یاسمی"
1451 | },
1452 | "location":{
1453 | "street":"7157 شهید گلپایگانی",
1454 | "city":"بابل",
1455 | "state":"بوشهر",
1456 | "postcode":11528
1457 | },
1458 | "email":"تینا.یاسمی@example.com",
1459 | "login":{
1460 | "username":"crazyostrich428",
1461 | "password":"garage",
1462 | "salt":"7vaM4dD5",
1463 | "md5":"e67b0ba4e522d4585466bc3e7f7acd0f",
1464 | "sha1":"a7a113b18d377561c50155e1eac18091ed72bb33",
1465 | "sha256":"0f5a3bb4a7902501612c5cc04b0ad425647c106030e894f0e6261bef45f9baa2"
1466 | },
1467 | "dob":"1949-04-20 14:27:17",
1468 | "registered":"2007-06-20 19:23:14",
1469 | "phone":"073-33044623",
1470 | "cell":"0951-173-7896",
1471 | "id":{
1472 | "name":"",
1473 | "value":null
1474 | },
1475 | "picture":{
1476 | "large":"https://randomuser.me/api/portraits/women/20.jpg",
1477 | "medium":"https://randomuser.me/api/portraits/med/women/20.jpg",
1478 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/20.jpg"
1479 | },
1480 | "nat":"IR"
1481 | },
1482 | {
1483 | "gender":"male",
1484 | "name":{
1485 | "title":"mr",
1486 | "first":"johnny",
1487 | "last":"thomas"
1488 | },
1489 | "location":{
1490 | "street":"3854 green rd",
1491 | "city":"centennial",
1492 | "state":"colorado",
1493 | "postcode":37365
1494 | },
1495 | "email":"johnny.thomas@example.com",
1496 | "login":{
1497 | "username":"heavydog142",
1498 | "password":"fitter",
1499 | "salt":"7asMzEoX",
1500 | "md5":"d1a65390366baa63652a46186d71d3c1",
1501 | "sha1":"642e475ce8e924a0f8be286ecbaaa0088f301bb7",
1502 | "sha256":"8584584142f7b224a90235c0acfe75263481b5284e8eef9685fa7f55e88073d2"
1503 | },
1504 | "dob":"1954-08-25 10:42:35",
1505 | "registered":"2012-12-27 04:03:56",
1506 | "phone":"(129)-162-9373",
1507 | "cell":"(815)-118-3713",
1508 | "id":{
1509 | "name":"SSN",
1510 | "value":"965-58-9877"
1511 | },
1512 | "picture":{
1513 | "large":"https://randomuser.me/api/portraits/men/2.jpg",
1514 | "medium":"https://randomuser.me/api/portraits/med/men/2.jpg",
1515 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/2.jpg"
1516 | },
1517 | "nat":"US"
1518 | },
1519 | {
1520 | "gender":"male",
1521 | "name":{
1522 | "title":"mr",
1523 | "first":"christopher",
1524 | "last":"morris"
1525 | },
1526 | "location":{
1527 | "street":"9726 daisy dr",
1528 | "city":"nowra",
1529 | "state":"western australia",
1530 | "postcode":6946
1531 | },
1532 | "email":"christopher.morris@example.com",
1533 | "login":{
1534 | "username":"purpleleopard490",
1535 | "password":"memorex",
1536 | "salt":"WB2SymKz",
1537 | "md5":"66df34d7b508d56e67fcae269f930bb5",
1538 | "sha1":"6b7c701a6bb5eee5227febed35b6fd5e91c2d77e",
1539 | "sha256":"75f9d63262e2f7b63c7883e3bafdc6ea863f8c94dbabeac8939cdc8846ab4596"
1540 | },
1541 | "dob":"1990-01-20 07:49:48",
1542 | "registered":"2007-11-29 04:01:02",
1543 | "phone":"07-5802-1020",
1544 | "cell":"0488-128-495",
1545 | "id":{
1546 | "name":"TFN",
1547 | "value":"832032869"
1548 | },
1549 | "picture":{
1550 | "large":"https://randomuser.me/api/portraits/men/35.jpg",
1551 | "medium":"https://randomuser.me/api/portraits/med/men/35.jpg",
1552 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/35.jpg"
1553 | },
1554 | "nat":"AU"
1555 | },
1556 | {
1557 | "gender":"male",
1558 | "name":{
1559 | "title":"mr",
1560 | "first":"ایلیا",
1561 | "last":"رضاییان"
1562 | },
1563 | "location":{
1564 | "street":"1160 میدان شهدا",
1565 | "city":"نیشابور",
1566 | "state":"خوزستان",
1567 | "postcode":10213
1568 | },
1569 | "email":"ایلیا.رضاییان@example.com",
1570 | "login":{
1571 | "username":"whitepeacock340",
1572 | "password":"1215",
1573 | "salt":"DY0jiiEe",
1574 | "md5":"e1b4f9bea3ebf886fe1e6385c49afe06",
1575 | "sha1":"d7ea744bfafea2daf0d4b5b9dadf86728d1984cd",
1576 | "sha256":"403df1bc692ea50b020064503d7d12e4dc6cebab89aeba10ea6ab7a6143ca100"
1577 | },
1578 | "dob":"1958-03-19 02:22:00",
1579 | "registered":"2005-12-19 17:37:20",
1580 | "phone":"047-21571965",
1581 | "cell":"0944-283-2404",
1582 | "id":{
1583 | "name":"",
1584 | "value":null
1585 | },
1586 | "picture":{
1587 | "large":"https://randomuser.me/api/portraits/men/6.jpg",
1588 | "medium":"https://randomuser.me/api/portraits/med/men/6.jpg",
1589 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/6.jpg"
1590 | },
1591 | "nat":"IR"
1592 | },
1593 | {
1594 | "gender":"male",
1595 | "name":{
1596 | "title":"mr",
1597 | "first":"mahmoud",
1598 | "last":"drenthen"
1599 | },
1600 | "location":{
1601 | "street":"6153 paardenveld",
1602 | "city":"purmerend",
1603 | "state":"noord-holland",
1604 | "postcode":18020
1605 | },
1606 | "email":"mahmoud.drenthen@example.com",
1607 | "login":{
1608 | "username":"whitekoala882",
1609 | "password":"zxc123",
1610 | "salt":"V6X9QvsT",
1611 | "md5":"36a63b6785aa44a1c4be629ac1a5c939",
1612 | "sha1":"8cf311a00edf5dfff07ffa1eb944745b970efbef",
1613 | "sha256":"b974b8de03f6efb6920b5ec28efee19db67f54716d14d19ed40a58aabc6d4606"
1614 | },
1615 | "dob":"1962-03-21 12:52:08",
1616 | "registered":"2010-07-03 23:43:31",
1617 | "phone":"(606)-283-8816",
1618 | "cell":"(652)-164-4006",
1619 | "id":{
1620 | "name":"BSN",
1621 | "value":"43152752"
1622 | },
1623 | "picture":{
1624 | "large":"https://randomuser.me/api/portraits/men/96.jpg",
1625 | "medium":"https://randomuser.me/api/portraits/med/men/96.jpg",
1626 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/96.jpg"
1627 | },
1628 | "nat":"NL"
1629 | },
1630 | {
1631 | "gender":"male",
1632 | "name":{
1633 | "title":"mr",
1634 | "first":"eli",
1635 | "last":"morris"
1636 | },
1637 | "location":{
1638 | "street":"2028 titahi bay road",
1639 | "city":"upper hutt",
1640 | "state":"taranaki",
1641 | "postcode":17909
1642 | },
1643 | "email":"eli.morris@example.com",
1644 | "login":{
1645 | "username":"silverrabbit543",
1646 | "password":"apache",
1647 | "salt":"1vb2zHi3",
1648 | "md5":"012392a91d163811ec25f6f0769381f8",
1649 | "sha1":"4b4c0e4c20d5915c3944a4791421d4c823434ae8",
1650 | "sha256":"c7f06e254de4d1e8ca3ccf1ff3beea93134cef8b94beb81825c431ec6661d024"
1651 | },
1652 | "dob":"1969-10-10 13:08:38",
1653 | "registered":"2013-08-14 18:23:38",
1654 | "phone":"(779)-630-3185",
1655 | "cell":"(637)-236-2277",
1656 | "id":{
1657 | "name":"",
1658 | "value":null
1659 | },
1660 | "picture":{
1661 | "large":"https://randomuser.me/api/portraits/men/50.jpg",
1662 | "medium":"https://randomuser.me/api/portraits/med/men/50.jpg",
1663 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/50.jpg"
1664 | },
1665 | "nat":"NZ"
1666 | },
1667 | {
1668 | "gender":"male",
1669 | "name":{
1670 | "title":"mr",
1671 | "first":"hugo",
1672 | "last":"jackson"
1673 | },
1674 | "location":{
1675 | "street":"9140 ronwood avenue",
1676 | "city":"tauranga",
1677 | "state":"wellington",
1678 | "postcode":84614
1679 | },
1680 | "email":"hugo.jackson@example.com",
1681 | "login":{
1682 | "username":"ticklishfish734",
1683 | "password":"pancake",
1684 | "salt":"GdAj6Gnc",
1685 | "md5":"698071f49899acca5348c16841ef9e63",
1686 | "sha1":"cdc877673e49aa8d3d71a31ba19dc8a44a673dd1",
1687 | "sha256":"093df6db8153e79e20878b142e3ff36747346d560cf8672019058c445831168c"
1688 | },
1689 | "dob":"1957-07-27 07:18:37",
1690 | "registered":"2002-12-03 01:57:57",
1691 | "phone":"(629)-494-7546",
1692 | "cell":"(155)-686-6174",
1693 | "id":{
1694 | "name":"",
1695 | "value":null
1696 | },
1697 | "picture":{
1698 | "large":"https://randomuser.me/api/portraits/men/39.jpg",
1699 | "medium":"https://randomuser.me/api/portraits/med/men/39.jpg",
1700 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/39.jpg"
1701 | },
1702 | "nat":"NZ"
1703 | },
1704 | {
1705 | "gender":"female",
1706 | "name":{
1707 | "title":"miss",
1708 | "first":"elya",
1709 | "last":"lopez"
1710 | },
1711 | "location":{
1712 | "street":"6981 rue du stade",
1713 | "city":"créteil",
1714 | "state":"vendée",
1715 | "postcode":97647
1716 | },
1717 | "email":"elya.lopez@example.com",
1718 | "login":{
1719 | "username":"lazyswan254",
1720 | "password":"master",
1721 | "salt":"JNCM59bo",
1722 | "md5":"6012d5b06421a07427b96956124fa28e",
1723 | "sha1":"2c64b42689418380ed4c6525da8f1b636f4ad2e1",
1724 | "sha256":"91edf6ca6aab5f88e3a675065eccc2a4fc8ca3b9104cefd29770f159f468f43e"
1725 | },
1726 | "dob":"1984-01-07 14:23:45",
1727 | "registered":"2003-05-27 02:55:51",
1728 | "phone":"02-69-54-91-89",
1729 | "cell":"06-15-97-10-75",
1730 | "id":{
1731 | "name":"INSEE",
1732 | "value":"284063370589 97"
1733 | },
1734 | "picture":{
1735 | "large":"https://randomuser.me/api/portraits/women/53.jpg",
1736 | "medium":"https://randomuser.me/api/portraits/med/women/53.jpg",
1737 | "thumbnail":"https://randomuser.me/api/portraits/thumb/women/53.jpg"
1738 | },
1739 | "nat":"FR"
1740 | },
1741 | {
1742 | "gender":"male",
1743 | "name":{
1744 | "title":"mr",
1745 | "first":"nixon",
1746 | "last":"patel"
1747 | },
1748 | "location":{
1749 | "street":"8537 elliot street",
1750 | "city":"blenheim",
1751 | "state":"canterbury",
1752 | "postcode":97383
1753 | },
1754 | "email":"nixon.patel@example.com",
1755 | "login":{
1756 | "username":"beautifulmeercat379",
1757 | "password":"sinner",
1758 | "salt":"hexhsAOY",
1759 | "md5":"a4dbaf29f66e2bfe49832f0c1fa95a5d",
1760 | "sha1":"0021a3753e3ba4225ef81bb5fe4f43573e2cea57",
1761 | "sha256":"a5544b331de656902a3dbc492f3b25f78cfadfb9597da84b92eb1151a5c23910"
1762 | },
1763 | "dob":"1964-05-24 17:50:56",
1764 | "registered":"2013-11-23 15:14:12",
1765 | "phone":"(574)-489-9456",
1766 | "cell":"(955)-946-7082",
1767 | "id":{
1768 | "name":"",
1769 | "value":null
1770 | },
1771 | "picture":{
1772 | "large":"https://randomuser.me/api/portraits/men/97.jpg",
1773 | "medium":"https://randomuser.me/api/portraits/med/men/97.jpg",
1774 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/97.jpg"
1775 | },
1776 | "nat":"NZ"
1777 | },
1778 | {
1779 | "gender":"male",
1780 | "name":{
1781 | "title":"mr",
1782 | "first":"janne",
1783 | "last":"baars"
1784 | },
1785 | "location":{
1786 | "street":"7970 sterrenburg",
1787 | "city":"huizen",
1788 | "state":"groningen",
1789 | "postcode":74926
1790 | },
1791 | "email":"janne.baars@example.com",
1792 | "login":{
1793 | "username":"beautifuldog187",
1794 | "password":"last",
1795 | "salt":"WTxMzvkM",
1796 | "md5":"b32b431585cb6a4379386a2e6fe22c1e",
1797 | "sha1":"8f0d0caf380532fbe09667029f9d2ea68a2c9586",
1798 | "sha256":"b75505d14dbcd8616ca225f0e1337d5c67cb7aeabf3cb89a0bdc414308f8bf23"
1799 | },
1800 | "dob":"1969-10-22 10:22:53",
1801 | "registered":"2008-01-07 20:53:40",
1802 | "phone":"(820)-764-8571",
1803 | "cell":"(355)-997-8038",
1804 | "id":{
1805 | "name":"BSN",
1806 | "value":"98409671"
1807 | },
1808 | "picture":{
1809 | "large":"https://randomuser.me/api/portraits/men/33.jpg",
1810 | "medium":"https://randomuser.me/api/portraits/med/men/33.jpg",
1811 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/33.jpg"
1812 | },
1813 | "nat":"NL"
1814 | },
1815 | {
1816 | "gender":"male",
1817 | "name":{
1818 | "title":"mr",
1819 | "first":"john",
1820 | "last":"wood"
1821 | },
1822 | "location":{
1823 | "street":"8889 highgate",
1824 | "city":"tauranga",
1825 | "state":"manawatu-wanganui",
1826 | "postcode":69259
1827 | },
1828 | "email":"john.wood@example.com",
1829 | "login":{
1830 | "username":"bigkoala901",
1831 | "password":"volley",
1832 | "salt":"lg2HfKQX",
1833 | "md5":"3f827aae4a72e0d6a7aafd9c2b773590",
1834 | "sha1":"32afc58b0afb832f996db193a822a30b92e49db4",
1835 | "sha256":"d070324588ef4da79e884757c1dae287958473e4084daffef4f4392ff01208fd"
1836 | },
1837 | "dob":"1956-05-31 11:28:49",
1838 | "registered":"2006-12-15 21:23:18",
1839 | "phone":"(100)-455-5811",
1840 | "cell":"(242)-012-8783",
1841 | "id":{
1842 | "name":"",
1843 | "value":null
1844 | },
1845 | "picture":{
1846 | "large":"https://randomuser.me/api/portraits/men/42.jpg",
1847 | "medium":"https://randomuser.me/api/portraits/med/men/42.jpg",
1848 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/42.jpg"
1849 | },
1850 | "nat":"NZ"
1851 | }
1852 | ];
1853 |
1854 | export default seedData;
--------------------------------------------------------------------------------
/server/seed/seed.js:
--------------------------------------------------------------------------------
1 | import seedData from './seed.data';
2 | import appConfig from '../config/config';
3 | import assert from 'assert';
4 |
5 |
6 | /**
7 | * @author Ahsan Ayaz
8 | * The function adds the seed data from seed.data.js into the database
9 | * On every restart/start of the server, the previous data will we be WIPED out and
10 | * seed data will be inserted.
11 | * To avoid this behavior, set seedData property to false in appConfig
12 | */
13 | const insertSeedData = (app) => {
14 | const { db } = app.mongo;
15 | const usersCollection = db.collection('users');
16 | usersCollection.remove({}, (err) => {
17 | assert.equal(null, err);
18 | usersCollection.insertMany(seedData, (err, result) => {
19 | assert.equal(null, err);
20 | console.log('seed data entered');
21 | });
22 | })
23 | }
24 |
25 |
26 | export default insertSeedData;
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { HomeComponent } from './containers/home/home.component';
4 | const routes: Routes = [
5 | {
6 | path: '',
7 | pathMatch: 'full',
8 | component: HomeComponent,
9 | children: []
10 | },
11 | { loadChildren: 'app/users/users.module#UsersModule', path: 'users' } // Lazy loaded `users` route
12 | ];
13 |
14 | @NgModule({
15 | imports: [RouterModule.forRoot(routes, {
16 | useHash: true
17 | })],
18 | exports: [RouterModule]
19 | })
20 | export class AppRoutingModule { }
21 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/app/app.component.scss
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 |
4 | import { AppComponent } from './app.component';
5 |
6 | describe('AppComponent', () => {
7 | beforeEach(async(() => {
8 | TestBed.configureTestingModule({
9 | imports: [
10 | RouterTestingModule
11 | ],
12 | declarations: [
13 | AppComponent
14 | ],
15 | }).compileComponents();
16 | }));
17 |
18 | it('should create the app', async(() => {
19 | const fixture = TestBed.createComponent(AppComponent);
20 | const app = fixture.debugElement.componentInstance;
21 | expect(app).toBeTruthy();
22 | }));
23 |
24 | it(`should have as title 'app'`, async(() => {
25 | const fixture = TestBed.createComponent(AppComponent);
26 | const app = fixture.debugElement.componentInstance;
27 | expect(app.title).toEqual('app');
28 | }));
29 |
30 | it('should render title in a h1 tag', async(() => {
31 | const fixture = TestBed.createComponent(AppComponent);
32 | fixture.detectChanges();
33 | const compiled = fixture.debugElement.nativeElement;
34 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
35 | }));
36 | });
37 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewContainerRef } from '@angular/core';
2 | import { ToastsManager } from 'ng2-toastr';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.scss']
8 | })
9 | export class AppComponent {
10 | constructor(private vcr: ViewContainerRef, private toastr: ToastsManager) {
11 | // Setting toastr's root container
12 | this.toastr.setRootViewContainerRef(vcr);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { ToastModule } from 'ng2-toastr/ng2-toastr';
4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
5 |
6 | import { AppRoutingModule } from './app-routing.module';
7 | import { AppComponent } from './app.component';
8 | import { HeaderComponent } from './components/header/header.component';
9 |
10 | // Bootstrap modules
11 | import { CollapseModule } from 'ngx-bootstrap/collapse';
12 | import { HomeComponent } from './containers/home/home.component';
13 |
14 |
15 |
16 | @NgModule({
17 | declarations: [
18 | AppComponent,
19 | HeaderComponent,
20 | HomeComponent
21 | ],
22 | imports: [
23 | BrowserModule,
24 | AppRoutingModule,
25 | CollapseModule.forRoot(),
26 | ToastModule.forRoot(),
27 | BrowserAnimationsModule
28 | ],
29 | providers: [],
30 | bootstrap: [AppComponent]
31 | })
32 | export class AppModule { }
33 |
--------------------------------------------------------------------------------
/src/app/components/header/header.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/components/header/header.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/app/components/header/header.component.scss
--------------------------------------------------------------------------------
/src/app/components/header/header.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HeaderComponent } from './header.component';
4 |
5 | describe('HeaderComponent', () => {
6 | let component: HeaderComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HeaderComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HeaderComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/components/header/header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewEncapsulation } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-header',
5 | templateUrl: './header.component.html',
6 | styleUrls: ['./header.component.scss'],
7 | encapsulation: ViewEncapsulation.None
8 | })
9 | export class HeaderComponent implements OnInit {
10 | isCollapsed = true;
11 | constructor() { }
12 |
13 | ngOnInit() {
14 | }
15 |
16 | /**
17 | * @author Ahsan Ayaz
18 | * Toggles the menu in lower screen widths
19 | */
20 | toggleCollapse() {
21 | this.isCollapsed = !this.isCollapsed;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/containers/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}!
4 |
5 |
6 |

7 |

8 |
9 |
--------------------------------------------------------------------------------
/src/app/containers/home/home.component.scss:
--------------------------------------------------------------------------------
1 | .home-container{
2 | margin-top: 30px;
3 | text-align: center;
4 | .logos{
5 | width: 630px;
6 | height: 300px;
7 | margin: 0 auto;
8 | display: flex;
9 | flex-direction: row;
10 | img{
11 | height: 100%;
12 | object-fit: contain;
13 | max-width: 280px;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/app/containers/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/containers/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html',
6 | styleUrls: ['./home.component.scss']
7 | })
8 | export class HomeComponent implements OnInit {
9 | title = 'Angular Fastify Starter';
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/users/add-user-modal/add-user-modal.component.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/app/users/add-user-modal/add-user-modal.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/app/users/add-user-modal/add-user-modal.component.scss
--------------------------------------------------------------------------------
/src/app/users/add-user-modal/add-user-modal.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AddUserModalComponent } from './add-user-modal.component';
4 |
5 | describe('AddUserModalComponent', () => {
6 | let component: AddUserModalComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ AddUserModalComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AddUserModalComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/users/add-user-modal/add-user-modal.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { BsModalRef } from 'ngx-bootstrap/modal';
3 | import { FormGroup, FormBuilder, Validators } from '@angular/forms';
4 | import { IAddUserForm } from '../users.models';
5 |
6 | // tslint:disable-next-line:max-line-length
7 | const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
8 |
9 | @Component({
10 | selector: 'app-add-user-modal',
11 | templateUrl: './add-user-modal.component.html',
12 | styleUrls: ['./add-user-modal.component.scss']
13 | })
14 | export class AddUserModalComponent implements OnInit {
15 | addUserForm: FormGroup;
16 | userForm: IAddUserForm = {
17 | email: '',
18 | password: ''
19 | };
20 | constructor(public bsModalRef: BsModalRef, private formBuilder: FormBuilder) { }
21 |
22 | ngOnInit() {
23 | // Set up the form values (you can add more later)
24 | this.addUserForm = this.formBuilder.group({
25 | email: ['', [Validators.required, Validators.pattern(emailRegex)]],
26 | password: ['', [Validators.required]]
27 | });
28 | }
29 |
30 | /**
31 | * @author Ahsan Ayaz
32 | * The function gets triggered on submission of the form when the user
33 | * clicks the save button.
34 | * The onFormSubmit function has to be bound by the parent using this modal
35 | * @param formValue
36 | */
37 | submitForm(formValue) {
38 | this.bsModalRef.content.onFormSubmit(formValue);
39 | this.bsModalRef.hide();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/users/user-list-item/user-list-item.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![]()
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{user?.location?.street}}
12 |
13 |
14 | {{user.phone}}
15 |
16 |
17 | {{user.email}}
18 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/src/app/users/user-list-item/user-list-item.component.scss:
--------------------------------------------------------------------------------
1 | .list-group-item{
2 | position: relative;
3 | .delete-btn{
4 | position: absolute;
5 | right: 10px;
6 | top: 10px;
7 | display: none;
8 | }
9 | &:hover{
10 | background: #eee;
11 | transition: 1s ease all;
12 |
13 | .delete-btn{
14 | cursor: pointer;
15 | display: block;
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/app/users/user-list-item/user-list-item.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UserListItemComponent } from './user-list-item.component';
4 |
5 | describe('UserListItemComponent', () => {
6 | let component: UserListItemComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ UserListItemComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(UserListItemComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/users/user-list-item/user-list-item.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-user-list-item',
5 | templateUrl: './user-list-item.component.html',
6 | styleUrls: ['./user-list-item.component.scss']
7 | })
8 | export class UserListItemComponent implements OnInit {
9 | @Input() user;
10 | @Output() onUserDelete = new EventEmitter<{userId: 'string'}>();
11 | constructor() { }
12 |
13 | ngOnInit() {
14 | }
15 |
16 | /**
17 | * @author Ahsan Ayaz
18 | * The function gets triggered when the user clicks the X button on a
19 | * user item under user lists. This emits an event upwards to parent component
20 | * with the user's id
21 | */
22 | deleteUser() {
23 | this.onUserDelete.emit({
24 | userId: this.user._id
25 | });
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/users/users-list/users-list.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/users/users-list/users-list.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/app/users/users-list/users-list.component.scss
--------------------------------------------------------------------------------
/src/app/users/users-list/users-list.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UsersListComponent } from './users-list.component';
4 |
5 | describe('UsersListComponent', () => {
6 | let component: UsersListComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ UsersListComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(UsersListComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/users/users-list/users-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-users-list',
5 | templateUrl: './users-list.component.html',
6 | styleUrls: ['./users-list.component.scss']
7 | })
8 | export class UsersListComponent implements OnInit {
9 | @Input() users;
10 | @Output() onUserDeleteEvent = new EventEmitter<{userId: string}>();
11 | constructor() { }
12 |
13 | ngOnInit() {
14 | }
15 |
16 | /**
17 | * @author Ahsan Ayaz
18 | * The function gets triggered when a delete event is fired on a list item and navigates to this component
19 | * @param $event {userId: string}
20 | */
21 | onUserDeleteHandler($event: {userId: string}) {
22 | this.onUserDeleteEvent.emit($event);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/users/users.component.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/app/users/users.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | display: block;
3 | }
4 |
5 | #usersPage{
6 | padding: 30px 20px;
7 | max-width: 500px;
8 | margin: 0 auto;
9 |
10 | .add-user-btn{
11 | position: fixed;
12 | right: 50px;
13 | bottom: 50px;
14 | z-index: 2;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/app/users/users.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UsersComponent } from './users.component';
4 |
5 | describe('DashboardComponent', () => {
6 | let component: UsersComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ UsersComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(UsersComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should be created', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/users/users.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { BsModalService } from 'ngx-bootstrap/modal';
3 | import { ToastsManager } from 'ng2-toastr/ng2-toastr';
4 | import 'rxjs/add/operator/first';
5 | import { BsModalRef } from 'ngx-bootstrap/modal/modal-options.class';
6 | import { AddUserModalComponent } from './add-user-modal/add-user-modal.component';
7 | import { UsersService } from './users.service';
8 |
9 | @Component({
10 | selector: 'app-users',
11 | templateUrl: 'users.component.html',
12 | styleUrls: ['users.component.scss']
13 | })
14 | export class UsersComponent implements OnInit {
15 | usersList;
16 | addUserModalRef: BsModalRef;
17 | constructor(
18 | private modalService: BsModalService,
19 | private usersService: UsersService,
20 | private toastr: ToastsManager
21 | ) { }
22 |
23 | ngOnInit() {
24 | this.usersService.getUsers()
25 | .first()
26 | .subscribe((response) => {
27 | this.usersList = response.users;
28 | });
29 | }
30 |
31 | /**
32 | * @author Ahsan Ayaz
33 | * Shows the add user modal
34 | */
35 | showAddUserModal() {
36 | this.addUserModalRef = this.modalService.show(AddUserModalComponent);
37 | this.addUserModalRef.content.onFormSubmit = this.onFormSubmit.bind(this);
38 | }
39 |
40 | /**
41 | * @author Ahsan Ayaz
42 | * This method is bound to the BsModalRef of the add user modal.
43 | * This gets triggered when the user submits that modal and the values from
44 | * the form inside that modal are received as parameters here.
45 | * Therefore, we can further send the data in addUser call using the service.
46 | */
47 | onFormSubmit(createUserFormValue) {
48 | this.usersService.addUser(createUserFormValue)
49 | .first()
50 | .subscribe(res => {
51 | this.toastr.success('User added successfully');
52 | this.usersList.unshift(res.user);
53 | }, err => {
54 | this.toastr.error('Could not add user');
55 | });
56 | }
57 |
58 | /**
59 | * @author Ahsan Ayaz
60 | * The function tirggers from the the child components and happens
61 | * when the user clicks the delete button on a user list item
62 | * @param $event
63 | */
64 | deleteUser($event) {
65 | console.log($event);
66 | this.usersService.deleteUser($event)
67 | .first()
68 | .subscribe(res => {
69 | this.toastr.success('User deleted successfully');
70 | this.usersList = this.usersList.filter(user => user._id !== $event.userId);
71 | }, err => {
72 | this.toastr.error('Could not delete user');
73 | });
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/app/users/users.models.ts:
--------------------------------------------------------------------------------
1 | export interface IAddUserForm {
2 | email: string;
3 | password: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/users/users.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4 | import { ModalModule } from 'ngx-bootstrap/modal';
5 | import { HttpModule } from '@angular/http';
6 | import { UsersComponent } from './users.component';
7 | import { RouterModule } from '@angular/router';
8 | import { routes } from './users.routes';
9 | import { UsersListComponent } from './users-list/users-list.component';
10 | import { UserListItemComponent } from './user-list-item/user-list-item.component';
11 | import { AddUserModalComponent } from './add-user-modal/add-user-modal.component';
12 | import { UsersService } from './users.service';
13 |
14 |
15 | @NgModule({
16 | imports: [
17 | CommonModule,
18 | RouterModule.forChild(routes),
19 | ModalModule.forRoot(),
20 | FormsModule,
21 | ReactiveFormsModule,
22 | HttpModule
23 | ],
24 | providers: [ UsersService ],
25 | entryComponents: [ AddUserModalComponent ],
26 | declarations: [ UsersComponent, UsersListComponent, UserListItemComponent, AddUserModalComponent ]
27 | })
28 | export class UsersModule { }
29 |
--------------------------------------------------------------------------------
/src/app/users/users.routes.ts:
--------------------------------------------------------------------------------
1 | import { Route } from '@angular/router';
2 | import { UsersComponent } from './users.component';
3 |
4 | export const routes: Route[] = [
5 | {
6 | path: '',
7 | component: UsersComponent
8 | }
9 | ];
10 |
--------------------------------------------------------------------------------
/src/app/users/users.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { UsersService } from './users.service';
4 |
5 | describe('UsersService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [UsersService]
9 | });
10 | });
11 |
12 | it('should be created', inject([UsersService], (service: UsersService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/users/users.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http } from '@angular/http';
3 | import { Observable } from 'rxjs/Observable';
4 | import 'rxjs/add/observable/of';
5 | import 'rxjs/add/operator/map';
6 |
7 | @Injectable()
8 | export class UsersService {
9 |
10 | constructor(private http: Http) { }
11 |
12 | /**
13 | * @author Ahsan Ayaz
14 | * The function fetches the users from the node server
15 | */
16 | getUsers() {
17 | return this.http.get('/api/v1/user')
18 | .map(res => res.json());
19 | }
20 |
21 | /**
22 | * @author Ahsan Ayaz
23 | * The function sends a delete user request to the server based on user Id
24 | * @param params {userId for now}
25 | */
26 | deleteUser(params) {
27 | return this.http.delete(`/api/v1/user/${params.userId}`)
28 | .map(res => res.json());
29 | }
30 |
31 | /**
32 | * @author Ahsan Ayaz
33 | * Sends an add user call to server which in result adds a user in the database
34 | * @param params {email and password for now}
35 | */
36 | addUser(params) {
37 | return this.http.post('/api/v1/user', params)
38 | .map(res => res.json());
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/angular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/assets/angular.png
--------------------------------------------------------------------------------
/src/assets/fastify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/assets/fastify.png
--------------------------------------------------------------------------------
/src/assets/ng-fastify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/assets/ng-fastify.png
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AhsanAyaz/angular-fastify-starter/26ac7a2385e3a5c01345bb30c5e8ecb1b79a45a1/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularFastifyStarter
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule);
12 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** Evergreen browsers require these. **/
41 | import 'core-js/es6/reflect';
42 | import 'core-js/es7/reflect';
43 |
44 |
45 | /**
46 | * Required to support Web Animations `@angular/animation`.
47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
48 | **/
49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
50 |
51 |
52 |
53 | /***************************************************************************************************
54 | * Zone JS is required by Angular itself.
55 | */
56 | import 'zone.js/dist/zone'; // Included with Angular CLI.
57 |
58 |
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
64 | /**
65 | * Date, currency, decimal and percent pipes.
66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
67 | */
68 | // import 'intl'; // Run `npm install --save intl`.
69 | /**
70 | * Need to import at least one locale-data with intl.
71 | */
72 | // import 'intl/locale-data/jsonp/en';
73 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare const __karma__: any;
17 | declare const require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "node"
11 | ]
12 | },
13 | "files": [
14 | "test.ts"
15 | ],
16 | "include": [
17 | "**/*.spec.ts",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2016",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "eofline": true,
15 | "forin": true,
16 | "import-blacklist": [
17 | true,
18 | "rxjs"
19 | ],
20 | "import-spacing": true,
21 | "indent": [
22 | true,
23 | "spaces"
24 | ],
25 | "interface-over-type-literal": true,
26 | "label-position": true,
27 | "max-line-length": [
28 | true,
29 | 140
30 | ],
31 | "member-access": false,
32 | "member-ordering": [
33 | true,
34 | {
35 | "order": [
36 | "static-field",
37 | "instance-field",
38 | "static-method",
39 | "instance-method"
40 | ]
41 | }
42 | ],
43 | "no-arg": true,
44 | "no-bitwise": true,
45 | "no-console": [
46 | true,
47 | "debug",
48 | "info",
49 | "time",
50 | "timeEnd",
51 | "trace"
52 | ],
53 | "no-construct": true,
54 | "no-debugger": true,
55 | "no-duplicate-super": true,
56 | "no-empty": false,
57 | "no-empty-interface": true,
58 | "no-eval": true,
59 | "no-inferrable-types": [
60 | true,
61 | "ignore-params"
62 | ],
63 | "no-misused-new": true,
64 | "no-non-null-assertion": true,
65 | "no-shadowed-variable": true,
66 | "no-string-literal": false,
67 | "no-string-throw": true,
68 | "no-switch-case-fall-through": true,
69 | "no-trailing-whitespace": true,
70 | "no-unnecessary-initializer": true,
71 | "no-unused-expression": true,
72 | "no-use-before-declare": true,
73 | "no-var-keyword": true,
74 | "object-literal-sort-keys": false,
75 | "one-line": [
76 | true,
77 | "check-open-brace",
78 | "check-catch",
79 | "check-else",
80 | "check-whitespace"
81 | ],
82 | "prefer-const": true,
83 | "quotemark": [
84 | true,
85 | "single"
86 | ],
87 | "radix": true,
88 | "semicolon": [
89 | true,
90 | "always"
91 | ],
92 | "triple-equals": [
93 | true,
94 | "allow-null-check"
95 | ],
96 | "typedef-whitespace": [
97 | true,
98 | {
99 | "call-signature": "nospace",
100 | "index-signature": "nospace",
101 | "parameter": "nospace",
102 | "property-declaration": "nospace",
103 | "variable-declaration": "nospace"
104 | }
105 | ],
106 | "typeof-compare": true,
107 | "unified-signatures": true,
108 | "variable-name": false,
109 | "whitespace": [
110 | true,
111 | "check-branch",
112 | "check-decl",
113 | "check-operator",
114 | "check-separator",
115 | "check-type"
116 | ],
117 | "directive-selector": [
118 | true,
119 | "attribute",
120 | "app",
121 | "camelCase"
122 | ],
123 | "component-selector": [
124 | true,
125 | "element",
126 | "app",
127 | "kebab-case"
128 | ],
129 | "use-input-property-decorator": true,
130 | "use-output-property-decorator": true,
131 | "use-host-property-decorator": true,
132 | "no-input-rename": true,
133 | "no-output-rename": true,
134 | "use-life-cycle-interface": true,
135 | "use-pipe-transform-interface": true,
136 | "component-class-suffix": true,
137 | "directive-class-suffix": true,
138 | "no-access-missing-member": true,
139 | "templates-use-public": true,
140 | "invoke-injectable": true
141 | }
142 | }
143 |
--------------------------------------------------------------------------------