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

TypeScript types go out

TypeScript types come in

Derive along the way

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

  • Full database 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

Tips

  • REST APIs can be generated from GraphQL APIs, but not the other way around
  • Backend for front-end pattern

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!

@mwarger

Slides + Code

Modern Approaches for End-to-End Type Safety Using TypeScript [KCDC 2022]

By Mat Warger

Modern Approaches for End-to-End Type Safety Using TypeScript [KCDC 2022]

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.

  • 706