Build a backend with TypeScript using Nest.js RICHARD MOOT - - PowerPoint PPT Presentation
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
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 out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications.
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
What does a Nest app look like?
$ npm i -g @nestjs/cli $ nest new project-name
app.module.ts
app.service.ts
app.controller.ts
main.ts
Real App Time – Order Ahead
square.service.ts
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;
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;
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;
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();
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();
16import { 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();
17import { 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();
18private 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, } ],
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, } ],
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, } ],
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, } ],
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
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
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
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
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()
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()
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'),
] } }); 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;
] } }); 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;
] } }); 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;
] } }); 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;
] } }); 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;
] } }); 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;
] } }); 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;
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();
37square.controller.ts
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
square.module.ts
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 {}
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 {}
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 {}
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 {}
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 {}
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 {}
Front End
Square POS
Recap
Building a Square Module for Order Ahead
- Use modules to separate out functionality
- Controllers handle HTTP layer
- Services handle your business logic
Adapt for serverless – main.ts
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
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);
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);
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);
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);
Add in GraphQL - app.module.ts
import { Module } from '@nestjs/common'; @Module({ imports: [], }) export class ApplicationModule {}
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; @Module({ imports: [ GraphQLModule.forRoot({ typePaths: ['./**/*.graphql'], }), ], }) export class ApplicationModule {}
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 }
Q&A
square.com