Modern Approaches for End-to-End Type Safety Using TypeScript

Startups

Enterprise

TypeScript

Using TypeScript

You should be using TypeScript

Types are documentation...

...from your present...

...to your future self.

Types
=
Tools

What is end-to-end?

Approaches to

Type-Safety

Code Generation

  • A tool is used to create derivative types or code from a source
  • The code is used in part of the application
  • Can be used in local development or as part of the build process

Type Inference

  • A type is inferred by TypeScript when an explicit type annotation is not provided
  • Works for primitive values, objects, function return types, etc.
  • Default parameter values
Stack Options
Infrastructure AWS CDK
SST
Pulumi
Database Prisma
EdgeDB
TypeORM
Frontend and Backend (API) Swagger
GraphQL
tRPC

End-to-End (Full-Stack)

Infrastructure

  • AWS CDK

  • SST

  • Pulumi

Infrastructure as Code

  • Infrastructure as Code (IaC) is the managing and provisioning of infrastructure through code instead of through manual processes.*
  • The code can be version controlled just like any other file so that changes can be tracked over time.
  • Yes, it can be written in TypeScript!

Adopt

  • You're already using AWS
  • You're not using AWS but you want to
  • You want or need infra-as-code as part of your project/organization

Avoid

  • You're not using AWS and you want to keep it that way
  • Smaller projects that don't need the buy-in
  • Another thing to learn

AWS CDK

AWS Cloud Development Kit - Website

import * as cdk from 'aws-cdk-lib';
import {NodejsFunction} from 'aws-cdk-lib/aws-lambda-nodejs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as path from 'path';

export class CdkStarterStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myFunction = new NodejsFunction(this, 'my-function', {
      memorySize: 1024,
      timeout: cdk.Duration.seconds(5),
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'main',
      entry: path.join(__dirname, `/../src/my-lambda/index.ts`),
    });
  }
}

Adopt

  • You want or need Serverless applications
  • AWS only
  • IaC with local development

Avoid

  • Serverless doesn't fit your architecture or use-case
  • You don't want to use AWS

SST

Serverless Stack - Website

import * as sst from "@serverless-stack/resources";

export default class MyStack extends sst.Stack {
  constructor(scope: sst.App, id: string, props?: sst.StackProps) {
    super(scope, id, props);

    // Create the HTTP API
    const api = new Api(stack, "Api", {
      routes: {
        "GET /notes": "src/list.handler",
        "GET /notes/{id}": "src/get.handler",
        "PUT /notes/{id}": "src/update.handler",
      },
    });

    // Show the API endpoint in the output
    stack.addOutputs({
      ApiEndpoint: api.url,
    });
  }
}

Adopt

  • You want IaC across one or more cloud providers
  • Provider-agnostic
  • Unit-testable IaC (example)

Avoid

  • Smaller projects that don't need the buy-in
  • Another thing to learn

Pulumi

Universal IaC - Website

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("my-bucket");

// Export the name of the bucket
export const bucketName = bucket.id;

Database

  • TypeORM

  • Prisma

  • EdgeDB

Adopt

  • Familiar with traditional ORM patterns and practices
  • Performance compared to Prisma
  • Full-featured - all-around good choice

Avoid

  • Not as declarative
  • More verbose than Prisma
    • Querying
    • Data-model
  • Only limit-offset pagination is supported

TypeORM

ORM a la Hibernate/Entity Framework - Website

Database

Adopt

Avoid

  • Custom SQL query support
  • Automatic database CRUD
  • Automatic API

Prisma

Prisma Client - Website

Database

Demo

Prisma Client for TypeScript

Adopt

  • New and Shiny
  • Throughput and latency are paramount (benchmark)

Avoid

  • Need to keep existing database infra
  • Existing performance is "good enough"
  • IMO - client is not as ergonomic as Prisma

EdgeDB

Graph-relational database - Website

Database

Demo

EdgeDB TypeScript Client

Back-end (API) to Front-end

  • Nothing 😬

  • Swagger

  • GraphQL

  • tRPC

Adopt

  • Just don't
  • If you do, be ready for pain

Avoid

  • Absolutely
  • If at all possible

Nothing

The Wild West

In all seriousness, consider that using TypeScript in a backend will pay massive dividends in run-time safety for your applications.

Back-end

Adopt

  • Other options are not an option
  • Basic prototypes
  • Consistent JSON
  • Run-time validation is an option

Avoid

  • Complex JSON 
  • Low sample size
  • If other solutions are available

QuickType

Convert JSON into code - Website

Front-end

Demo

Adopt

  • Existing (legacy) backends
  • Cannot adopt GraphQL
  • Smaller transition from non-swagger API

Avoid

  • Types are only as good as your swagger
  • Auto-gen is not always done or possible
  • Versioning problems inherent to REST APIs

Swagger

OpenAPI Specification - Website

Back-end

Demo

Resources

Adopt

  • API adheres to Swagger 2.0 or OpenAPI 3.0 spec
  • Tools used in projects can leverage types

Avoid

  • Types are only as good as your swagger

Swagger

OpenAPI Specification - Website

Front-end

Demo

Adopt

  • Already using Redux
  • React-query behavior within an established library
  • Supports OpenAPI and GraphQL

Avoid

  • Sworn off Redux?
  • React-query is an option
  • Provided structure is not needed

Redux-Toolkit

RTK-query Codegen - Website

Front-end

Demo

RTK Codegen

Adopt

  • Full type-safety
  • Separate teams
  • Established contacts

Avoid

  • Adoption/learning curve
  • Slight increase in complexity

GraphQL

GraphQL Specification - Website

Back-end

Demo

Now Playing Apollo GraphQL Server

Resources

Adopt

  • GraphQL back-end
  • Perfect type-safety
  • Autocomplete all the things

Avoid

  • Code generation is impractical
  • Backend is not using GraphQL

GraphQL

Multiple Options - Website

Front-end

Demo

Now Playing GraphQL React App

Adopt

  • Able to use a mono-repo
  • Type-safety without code generation
  • Backend is TS

Avoid

  • You need to use types outside the project
  • You need types in other languages
  • Your projects are in different repositories

tRPC

End-to-end typesafe APIs - Website

Back-end

Demo

trpc-server -> tmdb-router

Adopt

  • tRPC back-end
  • Perfect type-safety
  • Autocomplete all the things
  • No code generation!

Avoid

  • Backend is not tRPC
    • Except if you use trpc-openapi...
  • Backend is not part of the same repository

tRPC

TypeScript RPC - Website

Front-end

Demo

trpc-client

Honorable Mentions

Building React Apps with TypeScript

Securing a GraphQL API with Apollo 3

Thank you!

Slides + Code

@mwarger

Modern Approaches for End-to-End Type Safety Using TypeScript

By Mat Warger

Modern Approaches for End-to-End Type Safety Using TypeScript

When developing front-end applications with TypeScript, a common pain point is synchronizing your API data types with your UI. In this session, we'll look at a number of tools and techniques for taming this complexity. Whether you need a REST API or a GraphQL API, you'll learn how to leverage a combination of types and code generation to modernize your development experience from the server to the client.

  • 889