Build a backend with TypeScript using Nest.js RICHARD MOOT - - PowerPoint PPT Presentation

build a backend with typescript using nest js
SMART_READER_LITE
LIVE PREVIEW

Build a backend with TypeScript using Nest.js RICHARD MOOT - - PowerPoint PPT Presentation

Build a backend with TypeScript using Nest.js RICHARD MOOT DEVELOPER EVANGELIST @ SQUARE What is NestJS? A progressive Node.js framework for building efficient, reliable and scalable server-side applications. Huh? Nest provides an


slide-1
SLIDE 1
slide-2
SLIDE 2

Build a backend with TypeScript using Nest.js

RICHARD MOOT DEVELOPER EVANGELIST @ SQUARE

slide-3
SLIDE 3 3

What is NestJS?

A progressive Node.js framework for building efficient, reliable and scalable server-side applications.

Huh?

Nest provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications.

slide-4
SLIDE 4 4

How is this different that just using Express?

  • Fully supports TypeScript
  • Structured (similar to Angular)
  • Extremely modular
  • Supports dependency injection
  • Platform agnostic
  • GraphQL, microservices, CLI, and OpenAPI support
slide-5
SLIDE 5 5

What does a Nest app look like?

$ npm i -g @nestjs/cli $ nest new project-name

slide-6
SLIDE 6 6

app.module.ts

slide-7
SLIDE 7 7

app.service.ts

slide-8
SLIDE 8 8

app.controller.ts

slide-9
SLIDE 9 9

main.ts

slide-10
SLIDE 10 10

Real App Time – Order Ahead

slide-11
SLIDE 11 11

square.service.ts

slide-12
SLIDE 12 12 Footnote

import { Injectable } from '@nestjs/common'; import { ConfigService } from '../config/config.service'; import * as SquareConnect from 'square-connect'; import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string;

slide-13
SLIDE 13 13 Footnote

import { Injectable } from '@nestjs/common'; import { ConfigService } from '../config/config.service'; import * as SquareConnect from 'square-connect'; import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string;

slide-14
SLIDE 14 14 Footnote

import { Injectable } from '@nestjs/common'; import { ConfigService } from '../config/config.service'; import * as SquareConnect from 'square-connect'; import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string;

slide-15
SLIDE 15 15

import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations();

slide-16
SLIDE 16

import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations();

16
slide-17
SLIDE 17

import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations();

17
slide-18
SLIDE 18

import { randomBytes } from 'crypto'; import { PaymentDto } from './dto/PaymentDto.dto'; import * as functions from 'firebase-functions'; @Injectable() export class SquareService { constructor(config: ConfigService) { this.accessToken = config.get("ACCESS_TOKEN"); const SquareClient = SquareConnect.ApiClient.instance; const oauth2 = SquareClient.authentications["oauth2"];

  • auth2.accessToken = this.accessToken;

} private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations();

18
slide-19
SLIDE 19 19 Footnote

private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations(); const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ],

slide-20
SLIDE 20 20 Footnote

private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations(); const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ],

slide-21
SLIDE 21 21 Footnote

private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations(); const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ],

slide-22
SLIDE 22 22 Footnote

private accessToken: string; async pay(paymentRequest: PaymentDto){ const locationsApi = new SquareConnect.LocationsApi(); const transactionsApi = new SquareConnect.TransactionsApi(); const ordersApi = new SquareConnect.OrdersApi(); const { locations } = await locationsApi.listLocations(); const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ],

slide-23
SLIDE 23 23 Footnote

const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email

slide-24
SLIDE 24 24 Footnote

const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email

slide-25
SLIDE 25 25 Footnote

const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email

slide-26
SLIDE 26 26 Footnote

const locationId = locations[0].id; try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email

slide-27
SLIDE 27 27 Footnote

try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email }, pickup_at: (new Date()).toISOString()

slide-28
SLIDE 28 28 Footnote

try { const { order } = await ordersApi.createOrder(locationId, { idempotency_key: randomBytes(48).toString('hex'),

  • rder: {

line_items: [ { quantity: '1', catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email }, pickup_at: (new Date()).toISOString()

slide-29
SLIDE 29 29 Footnote

catalog_object_id: paymentRequest.item.variations[0].id, } ], fulfillments: [ { type: 'PICKUP', state: 'PROPOSED', pickup_details: { recipient: { display_name: paymentRequest.customer.name, email: paymentRequest.customer.email }, pickup_at: (new Date()).toISOString() } } ] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'),

slide-30
SLIDE 30 30 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-31
SLIDE 31 31 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-32
SLIDE 32 32 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-33
SLIDE 33 33 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-34
SLIDE 34 34 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-35
SLIDE 35 35 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-36
SLIDE 36 36 Footnote

] } }); const { transaction } = await transactionsApi.charge(locationId, { idempotency_key: randomBytes(48).toString('hex'), card_nonce: paymentRequest.nonce,

  • rder_id: order.id,

delay_capture: true, amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e;

slide-37
SLIDE 37

amount_money: { amount: 100, currency: 'USD' } }) return { id: transaction.id }; } catch (e) { console.log(e); throw e; } } async getCatalogItems(){ const catalogApi = new SquareConnect.CatalogApi(); try { const { objects } = await catalogApi.listCatalog();

37
slide-38
SLIDE 38 38

square.controller.ts

slide-39
SLIDE 39 39

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-40
SLIDE 40 40

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-41
SLIDE 41 41

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-42
SLIDE 42 42

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-43
SLIDE 43 43

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-44
SLIDE 44 44

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-45
SLIDE 45 45

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-46
SLIDE 46 46

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-47
SLIDE 47 47

import { Controller, Post, Body, Get } from '@nestjs/common'; import { SquareService } from './square.service'; import { PaymentDto } from './dto/PaymentDto.dto'; @Controller('square') export class SquareController { constructor(private readonly squareService: SquareService){} @Post('pay') async pay(@Body() paymentRequest: PaymentDto){ return this.squareService.pay(paymentRequest); } @Get('catalog') async getCatalogItems(){ return this.squareService.getCatalogItems(); } }

slide-48
SLIDE 48 48

square.module.ts

slide-49
SLIDE 49 49

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-50
SLIDE 50 50

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-51
SLIDE 51 51

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-52
SLIDE 52 52

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-53
SLIDE 53 53

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-54
SLIDE 54 54

import { Module } from '@nestjs/common'; import { SquareService } from './square.service'; import { ConfigModule } from '../config/config.module'; import { SquareController } from './square.controller'; @Module({ imports: [ConfigModule], providers: [SquareService], controllers: [SquareController], }) export class SquareModule {}

slide-55
SLIDE 55 55

Front End

slide-56
SLIDE 56 56
slide-57
SLIDE 57 57
slide-58
SLIDE 58 58

Square POS

slide-59
SLIDE 59 59
slide-60
SLIDE 60 60

Recap

slide-61
SLIDE 61 61

Building a Square Module for Order Ahead

  • Use modules to separate out functionality
  • Controllers handle HTTP layer
  • Services handle your business logic
slide-62
SLIDE 62 62 Footnote

Adapt for serverless – main.ts

slide-63
SLIDE 63 63

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();

slide-64
SLIDE 64 64

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as functions from 'firebase-functions'; import * as express from 'express'; import { Express } from 'express'; const server: Express = express(); const startNestApplication = async (expressInstance: Express) => { const app = await NestFactory.create(AppModule, expressInstance); await app.init(); } startNestApplication(server); export const square = functions.https.onRequest(server);

slide-65
SLIDE 65 65

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as functions from 'firebase-functions'; import * as express from 'express'; import { Express } from 'express'; const server: Express = express(); const startNestApplication = async (expressInstance: Express) => { const app = await NestFactory.create(AppModule, expressInstance); await app.init(); } startNestApplication(server); export const square = functions.https.onRequest(server);

slide-66
SLIDE 66 66

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as functions from 'firebase-functions'; import * as express from 'express'; import { Express } from 'express'; const server: Express = express(); const startNestApplication = async (expressInstance: Express) => { const app = await NestFactory.create(AppModule, expressInstance); await app.init(); } startNestApplication(server); export const square = functions.https.onRequest(server);

slide-67
SLIDE 67 67

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as functions from 'firebase-functions'; import * as express from 'express'; import { Express } from 'express'; const server: Express = express(); const startNestApplication = async (expressInstance: Express) => { const app = await NestFactory.create(AppModule, expressInstance); await app.init(); } startNestApplication(server); export const square = functions.https.onRequest(server);

slide-68
SLIDE 68 68

Add in GraphQL - app.module.ts

slide-69
SLIDE 69 69

import { Module } from '@nestjs/common'; @Module({ imports: [], }) export class ApplicationModule {}

slide-70
SLIDE 70 70

import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; @Module({ imports: [ GraphQLModule.forRoot({ typePaths: ['./**/*.graphql'], }), ], }) export class ApplicationModule {}

slide-71
SLIDE 71 71

type Query { getCats: [Cat] cat(id: ID!): Cat } type Mutation { createCat(createCatInput: CreateCatInput): Cat } type Cat { id: Int name: String age: Int } input CreateCatInput { name: String age: Int }

slide-72
SLIDE 72

Q&A

slide-73
SLIDE 73

square.com