---
# System prepended metadata

title: Usthing backend guideline
tags: [Backend T Documentation, Usthing Web]

---

---
title: 'Project documentation template'
disqus: hackmd
---
Usthing Web Backend Tutorial (Map)
===

[TOC]

## Functional >> imperative -> js dev
- functional: describe the function of this function
- imperative: describe how to perform this function
- https://stackoverflow.com/questions/17826380/what-is-difference-between-functional-and-imperative-programming-languages
![](https://i.imgur.com/nuOgNSB.png)


## Functional programming helper libraries  
reference : 
https://github.com/stoeffel/awesome-fp-js
### Lodash
https://lodash.com/
map : iteration
```jsx=
import _ from "lodash"

function square(n) {
  return n * n;
}
 
_.map([4, 8], square);
// => [16, 64]
 
_.map({ 'a': 4, 'b': 8 }, square);
// => [16, 64] (iteration order is not guaranteed)
 
var users = [
  { 'user': 'barney' },
  { 'user': 'fred' }
];
 
// The `_.property` iteratee shorthand.
_.map(users, 'user');
// => ['barney', 'fred']
```
flow : integrate multiple functions
```jsx=
function square(n) {
  return n * n;
}
 
var addSquare = _.flow([_.add, square]);
addSquare(1, 2);
// => 9
```

groupBy : 

```jsx=
_.groupBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }
 
// The `_.property` iteratee shorthand.
_.groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }
```





### Basic Shell Operations
#### Windows(don't care)
#### Linux: dev / prod environment
https://www.digitalocean.com/community/tutorials/linux-commands
![](https://i.imgur.com/inWp4Mz.png)

### Vim : Terminal Text Editor
learning:
https://danielmiessler.com/study/vim/
cheatsheet:
https://vim.rtorr.com/

### Git : Version Control
https://the-turing-way.netlify.app/reproducible-research/vcs/vcs-git-summary.html
![](https://i.imgur.com/6dUCdXZ.png)

### Docker: Virtual Environment
>Official Doc : https://docs.docker.com/get-started/03_updating_app/
>CheatSheet : https://dockerlabs.collabnix.com/docker/cheatsheet/
![](https://i.imgur.com/Kd0xRDF.png)

### Nginx : Web Server
```nginx=
user       www www;  ## Default: nobody
worker_processes  5;  ## Default: 1
error_log  logs/error.log;
pid        logs/nginx.pid;
worker_rlimit_nofile 8192;

events {
  worker_connections  4096;  ## Default: 1024
}

http {
  include    conf/mime.types;
  include    /etc/nginx/proxy.conf;
  include    /etc/nginx/fastcgi.conf;
  index    index.html index.htm index.php;

  default_type application/octet-stream;
  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log   logs/access.log  main;
  sendfile     on;
  tcp_nopush   on;
  server_names_hash_bucket_size 128; # this seems to be required for some vhosts

  server { # php/fastcgi
    listen       80;
    server_name  domain1.com www.domain1.com;
    access_log   logs/domain1.access.log  main;
    root         html;

    location ~ \.php$ {
      fastcgi_pass   127.0.0.1:1025;
    }
  }

  server { # simple reverse-proxy
    listen       80;
    server_name  domain2.com www.domain2.com;
    access_log   logs/domain2.access.log  main;

    # serve static files
    location ~ ^/(images|javascript|js|css|flash|media|static)/  {
      root    /var/www/virtual/big.server.com/htdocs;
      expires 30d;
    }

    # pass requests for dynamic content to rails/turbogears/zope, et al
    location / {
      proxy_pass      http://127.0.0.1:8080;
    }
  }
    
  upstream big_server_com {
    server 127.0.0.3:8000 weight=5;
    server 127.0.0.3:8001 weight=5;
    server 192.168.0.1:8000;
    server 192.168.0.1:8001;
  }

  server { # simple load balancing
    listen          80;
    server_name     big.server.com;
    access_log      logs/big.server.access.log main;

    location / {
      proxy_pass      http://big_server_com;
    }
  }
}
```

### Hosting Providers
>e.g : Godaddy

https://www.quicksprout.com/best-web-hosting/

#### DNS(Domain Name System) Record
![](https://i.imgur.com/OLLdi9j.png)
#### Types
![](https://i.imgur.com/qD1LLXF.png)
![](https://i.imgur.com/Hkd7Txx.png)
![](https://i.imgur.com/lsrWz9d.png)




### SSH: Remote Server Accessing
https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#ssh-overview


Coding Patterns
---
### Early Return
#### Avoid

```jsx=
function handleClick (event) {
	// Make sure clicked element has the .save-data class
	if (event.target.matches('.save-data')) {
		// Get the value of the [data-id] attribute
		let id = event.target.getAttribute('data-id');
		// Make sure there's an ID
		if (id) {
			// Get the user token from localStorage
			let token = localStorage.getItem('token');
			// Make sure there's a token
			if (token) {
				// Save the ID to localStorage
				localStorage.setItem(`${token}_${id}`, true);
			}
		}
	}
}
```

#### Prefer
```jsx=
function handleClick (event) {
	// Make sure clicked element has the .save-data class
	if (!event.target.matches('.save-data')) return;

	// Get the value of the [data-id] attribute
	let id = event.target.getAttribute('data-id');
	if (!id) return;

	// Get the user token from localStorage
	let token = localStorage.getItem('token');
	if (!token) return;

	// Save the ID to localStorage
	localStorage.setItem(`${token}_${id}`, true);
}

```
### Try / Catch Block

```jsx=
try {
  tryStatements
} catch (exceptionVar) {
  catchStatements
} finally {
  finallyStatements
}
```
### OOP

Basic Node Server
---
```jsx
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

```
> Simplest backend Node Server

![](https://i.imgur.com/BHBIddH.png)
> Backend JS Basic Skills



**HTTP Basics
---
checkout CORS / authentication / caching / cookies / redirects and Headers
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP


**Rest
---
#### Methods
```gherkin=
GET /path
POST /path
PUT /path
DELETE /path
```
#### Status Code
https://restfulapi.net/http-status-codes/
>1xx: Informational – Communicates transfer protocol-level information.
2xx: Success – Indicates that the client’s request was accepted successfully.
3xx: Redirection – Indicates that the client must take some additional action in order to complete their request.
4xx: Client Error – This category of error status codes points the finger at clients.
5xx: Server Error – The server takes responsibility for these error status codes.

#### Headers
https://www.soapui.org/learn/api/understanding-rest-headers-and-parameters/
>Authorization: Carries credentials containing the authentication information of the client for the resource being requested.

>WWW-Authenticate: This is sent by the server if it needs a form of authentication before it can respond with the actual resource being requested. Often sent along with a response code of 401, which means ‘unauthorized’.

>Accept-Charset: This is a header which is set with the request and tells the server about which character sets are acceptable by the client.

>Content-Type:  Indicates the media type (text/html or text/JSON) of the response sent to the client by the server, this will help the client in processing the response body correctly.

>Cache-Control: This is the cache policy defined by the server for this response, a cached response can be stored by the client and re-used till the time defined by the Cache-Control header.

#### Practices
https://stackoverflow.com/questions/4024271/rest-api-best-practices-where-to-put-parameters

**NestJS
---
#### Decorators
```jsx=
@Get(/path)
@Post(/path)
@Patch(/path)
@Delete(/path)
```


```gherkin=
@Post()
create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
}

@Get()
findAll() {
    return this.usersService.findAll();
}

@Get(':id')
findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
}

@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
}

@Delete(':id')
remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
}

```
> Read more about NestJS here: https://docs.nestjs.com/


**Mongoose (ODM) for Mongo Database
---
>Official Doc :https://mongoosejs.com/docs/guide.html
#### Basic Usage
```jsx
//Define schema
import { Model, Schema, HydratedDocument, model } from 'mongoose';

interface IUser {
  firstName: string;
  lastName: string;
}

interface IUserMethods {
  fullName(): string;
}

interface UserModel extends Model<IUser, {}, IUserMethods> {
  createWithFullName(name: string): Promise<HydratedDocument<IUser, IUserMethods>>;
}

const schema = new Schema<IUser, UserModel, IUserMethods>({
  firstName: { type: String, required: true },
  lastName: { type: String, required: true }
});
// invoked directly by a Model 
schema.static('createWithFullName', function createWithFullName(name: string) {
  const [firstName, lastName] = name.split(' ');
  return this.create({ firstName, lastName });
});
//invoked by an instance of a Mongoose document
schema.method('fullName', function fullName(): string {
  return this.firstName + ' ' + this.lastName;
});

const User = model<IUser, UserModel>('User', schema);

User.createWithFullName('Jean-Luc Picard').then(doc => {
  console.log(doc.firstName); // 'Jean-Luc'
  doc.fullName(); // 'Jean-Luc Picard'
});
```
#### Available Schema Types
```jsx=
const schema = new Schema({
  name: String,
  binary: Buffer,
  living: Boolean,
  updated: { type: Date, default: Date.now },
  age: { type: Number, min: 18, max: 65 },
  mixed: Schema.Types.Mixed,
  _someId: Schema.Types.ObjectId,
  decimal: Schema.Types.Decimal128,
  array: [],
  ofString: [String],
  ofNumber: [Number],
  ofDates: [Date],
  ofBuffer: [Buffer],
  ofBoolean: [Boolean],
  ofMixed: [Schema.Types.Mixed],
  ofObjectId: [Schema.Types.ObjectId],
  ofArrays: [[]],
  ofArrayOfNumbers: [[Number]],
  nested: {
    stuff: { type: String, lowercase: true, trim: true }
  },
  map: Map,
  mapOfString: {
    type: Map,
    of: String
  }
});
```
#### Schema Type Options
![](https://i.imgur.com/uS6RJrp.png)

#### Queries
![](https://i.imgur.com/00DxInr.png)


ODM VS ORM (Object Relational Mapping)
https://tangledguy.hashnode.dev/odm-vs-orm

JWT(Json Web Tokens)
---
https://jwt.io/introduction
>Authorization
>Secure Information Exchange

Express (lightweight Node FrameWork)
---
>Official DOC : https://expressjs.com/en/guide/routing.html
>Examples : https://expressjs.com/en/starter/examples.html
#### MiddleWare
```jsx=
const express = require('express')
const cookieParser = require('cookie-parser')
const cookieValidator = require('./cookieValidator')

const app = express()

async function validateCookies (req, res, next) {
  await cookieValidator(req.cookies)
  next()
}

app.use(cookieParser())

app.use(validateCookies)

// error handler
app.use((err, req, res, next) => {
  res.status(400).send(err.message)
})

app.listen(3000)
```
#### Routing
```jsx
const express = require('express')
const app = express()

//*foo* is the custom input
app.*METHOD*('*PATH*', (req, res) => {
  *function body*
})

// respond with "hello world" when a GET request is made to the homepage
app.get('/', (req, res) => {
  res.send('hello world')
})

app.get('/users/:userId/books/:bookId', (req, res) => {
  res.send(req.params)
})
```
>Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }

#### Response Methods
![](https://i.imgur.com/nLVdrBk.png)

Jest
---
>Official Doc : https://jestjs.io/docs/getting-started
>Functional Testing : https://katalon.com/resources-center/blog/functional-testing
#### Test With Mongo
```jsx=
const {MongoClient} = require('mongodb');

describe('insert', () => {
  let connection;
  let db;

  beforeAll(async () => {
    connection = await MongoClient.connect(globalThis.__MONGO_URI__, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    db = await connection.db(globalThis.__MONGO_DB_NAME__);
  });

  afterAll(async () => {
    await connection.close();
  });

  it('should insert a doc into collection', async () => {
    const users = db.collection('users');

    const mockUser = {_id: 'some-user-id', name: 'John'};
    await users.insertOne(mockUser);

    const insertedUser = await users.findOne({_id: 'some-user-id'});
    expect(insertedUser).toEqual(mockUser);
  });
});
```
#### Snapshot Testing
> make sure your UI does not change unexpectedly
```jsx=
import renderer from 'react-test-renderer';
import Link from '../Link';

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.facebook.com">Facebook</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});
```
https://jestjs.io/docs/snapshot-testing

#### Test With React 
Component (Link.js)
```jsx=
import {useState} from 'react';

const STATUS = {
  HOVERED: 'hovered',
  NORMAL: 'normal',
};

export default function Link({page, children}) {
  const [status, setStatus] = useState(STATUS.NORMAL);

  const onMouseEnter = () => {
    setStatus(STATUS.HOVERED);
  };

  const onMouseLeave = () => {
    setStatus(STATUS.NORMAL);
  };

  return (
    <a
      className={status}
      href={page || '#'}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    >
      {children}
    </a>
  );
}
```
Link.test.js
```jsx=
import renderer from 'react-test-renderer';
import Link from '../Link';

it('changes the class when hovered', () => {
  const component = renderer.create(
    <Link page="http://www.facebook.com">Facebook</Link>,
  );
  let tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  renderer.act(() => {
    tree.props.onMouseEnter();
  });
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  renderer.act(() => {
    tree.props.onMouseLeave();
  });
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});
```
>https://jestjs.io/docs/tutorial-react


Caching
---
### Redis
>https://www.npmjs.com/package/redis
```jsx=
import { createClient } from 'redis';

const client = createClient({
  url: 'redis://alice:foobared@awesome.redis.server:6380'
});

client.on('error', err => console.log('Redis Client Error', err));

await client.connect();

await client.set('key', 'value');
const value = await client.get('key');
await client.disconnect();
```



## Appendix and FAQ

:::info
**Find this document incomplete?** Leave a comment!
:::

General Concepts:
https://www.freecodecamp.org/news/have-an-idea-want-to-build-a-product-from-scratch-heres-a-checklist-of-things-you-should-go-through-in-your-backend-software-architecture/

Usthing Backend Reference**:
https://github.com/USThing/Backend-Refs

Usthing Backend Team Guidelines:
https://docs.google.com/document/d/1ZhLhc07_gENLfZUW-GHWbnHJP9HR__bhSM4NRI1cW5Y/edit#

Backend Team Docker Tutorial:
https://docs.google.com/document/d/1507FQISk8g86yrQFX85A_pFHjNqPoeRcyzatwmOYgXo/edit

Mongo Files(To be used) :
https://www.mongodb.com/docs/database-tools/mongofiles/#std-option-mongofiles.--local

Node Official:
https://nodejs.dev/en/learn/introduction-to-nodejs/

Usthing OAuth:
https://github.com/USThing/usthing-oauth-api

Apply Azure Developer (all itsc apis) ** :
https://docs.google.com/document/d/1JY7k1Dh7S7nDzyLQURkKCV4J4YWlFdm/edit

Backend Team Gdrive ** :
https://drive.google.com/drive/u/1/folders/1pu3lyfeXttbX12Xs05oR6ZZTs99UPzPN

Practices:
https://github.com/futurice/backend-best-practices

###### tags: `Usthing Web` `Backend T Documentation`