--- 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`