Take a Rest from REST

β€” Aleksandra Sikora, @aleksandrasays

whoami

- full-stack engineer,
- rock climber,
- organizer of
Β  WrocΕ‚aw TypeScript

previously
- Blitz.js Maintainer
- Hasura Console Tech Lead

🐦 @aleksandrasays
πŸ™ @beerose
🐘 @aleksandra@mas.to
🌎 https://aleksandra.codes

What is an API?

Why are we talking about APIs?

πŸ₯΄

Server & Client

Things get complicated

😭

API Layer Problems

Boilerplate

Lost typesafety

Repetitive error handling

= additional complexity

πŸ€“

API Layer Solutions

REST

SOAP

GraphQL

πŸ€”

API Layer Problems

Fullstack TypeScript app

TypeScript

TypeScript

API Layer Problems

tRPC: query & mutation procedures

Remix: loader pattern

Blitz RPC: query & mutation resolvers

React: Server-side components

API Layer Problems

tRPC: query & mutation procedures

Remix: loader pattern

Blitz RPC: query & mutation resolvers

React: Server-side components

⚠️ it doesn't mean NO API layer

API Layer Problems

tRPC: query & mutation procedures

Remix: loader pattern

Blitz RPC: query & mutation resolvers

React: Server-side components

What is the goal of this talk?

To show you alternative solutions to REST and GraphQL that can let you iterate faster and make fullstack web development easier.

What is the goal of this talk?

You can take a rest from REST, but you don't have to...

What is the goal of this talk?

You can take a rest from REST, but you don't have to...

New solutions don’t mean that the previous ones are bad.

You can take a rest from REST, but you don't have to...

RPC

1981

What is RPC?

// one-computer.js

function welcome(name) {
  return `Hello, ${name}!`
}

const greeting = welcome("JSConf Chile")
//    ^ "Hello, JSConf Chile!"

What is RPC?

// server.js

function welcome(name) {
  return `Hello, ${name}!`
}

startImaginaryServer({ welcome })
// client.js

const greeting = await fetch(
  `https://aleksandra.says/rpc/welcome`,
  { body: JSON.stringify("JSConf Chile") }
)

RPC is the practice of remotely calling functions

😌

πŸ₯΄

  • Client & server tightly coupled
  • Having to use the same language
  • Need to learn all the procedure names
  • Having to use multi-threaded servers
  • Parameters marshalling
  • Exception handling

Problems with RPC

What's next πŸ‘‰

CORBA

1991

NOT Γ‡ORBA

AND NOT COBRA

module Finance {
  typedef sequence<string> StringSeq;
  struct AccountDetails {
    string     name;
    StringSeq  address;
    long       account_number;
    double     current_balance;
  };
  exception insufficientFunds { };
  interface Account {
    void deposit(in double amount);
    void withdraw(in double amount)
                        raises(insufficientFunds);
    readonly attribute AccountDetails details;
  };
};

IDL

  • Complexity
  • Steep learning curve
  • Mapping problems
  • Name confused with a poisonous snake

Problems with CORBA

What's next πŸ‘‰

SOAP

1998

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetUser>
      <m:UserId>123</m:UserId>
    </m:GetUser>
  </soap:Body>
</soap:Envelope>
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
  <soap:Body>
    <m:GetUserResponse>
      <m:Username>Tony Stark</m:Username>
    </m:GetUserResponse>
  </soap:Body>
</soap:Envelope>
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetUser>
      <m:UserId>123</m:UserId>
    </m:GetUser>
  </soap:Body>
</soap:Envelope>
  • Heavy, requires more bandwidth
  • POST = no cache on HTTP layer
  • Tightly coupled with server
  • Inflexible

Problems with SOAP

What's next πŸ‘‰

REST

2000

When the web started changing

  • Set of architectural constraints

  • Intended to be consumed by multiple clients

REST APIs

Can request and update resources

Exposes resources

REST APIs

  • Set of architectural constraints

  • Intended to be consumed by multiple clients

SOAP vs. REST

Operation RPC REST
Login POST /login POST /sessions
Logout POST /logout DELETE /sessions
Get user by id GET /getUser?id=123 GET /users/123
Get user's todo items GET /getTodos?userId=123 GET /users/123/todos
Add new todo item POST /addTodo POST users/123/todos
Update todo item POST /updateTodo PUT /todos/1Β 
Delete todo item POST /deteteTodo DELETE /todos/1

RPC vs. REST

RPC over HTTP using JSON

RESTful

"REST"

  • Over fetching
  • Big payloads
  • n+1 problem
  • Limiting constraints
  • No end-to-end typesafety

Problems with REST

What's next πŸ‘‰

GraphQL

2012

REST API

GraphQL API

API

App

GET users/
GET tasks/
GET tags/

API

App

POST graphql/
Body:
{ "query": "query { users {...} }" }

vs

Client controls the data it gets

User 1

Task 1

Task 2

Tag 1

Tag 2

query {
  user(id: 1) {
    name
    tasks {
      name
      status
      tags {
       id
      }
    }
  }
}

name

surname

age

status

name

priority

name

priority

status

description

id

Tag 3

id

description

id

description

id

  • Same POST-caching problem as in SOAP
  • You have to generate types
  • If you use tools like Hasura,
    you push a lot of domain logic to frontend
  • Otherwise β€” boilerplate!

Problems with GraphQL

at least until stuff like Max Stoiber's GraphQL CDN popped up

  • Same POST-caching problem as in SOAP
  • You have to generate types
  • If you use tools like Hasura,
    you push a lot of domain logic to frontend
  • Otherwise β€” boilerplate!

Problems with GraphQL

What's next πŸ‘‰

RPC

1981Β 2022

Revisiting the original promise of RPC

γ€ž

As TypeScript and static typing increasingly becomes a best practice in web development, API contracts present a major pain point. We need better ways to statically typeΒ our API endpoints and share those typesΒ between our client and server (or server-to-server).

– tRPC docs

// server.ts

import db from "prisma"

async function welcome(userId: string) {
  const user = await db.users.findFirst({ where: { id: userId }})
  
  return `Welcome again, ${user.name}!`
}

startImaginaryServer({ welcome })
// client.ts

const greeting = await fetch(
  `https://aleksandra.says/rpc/welcome`,
  { body: JSON.stringify(1) }
)

Type safe backend

Unsafe frontend

// server.js

function welcome(name) {
  return `Hello, ${name}!`
}

startImaginaryServer({ welcome })

❌

Type safe backend

Unsafe frontend

// server.ts

import db from "prisma"

async function welcome(userId: string) {
  const user = await db.users.findFirst({ where: { id: userId }})
  
  return `Welcome again, ${user.name}!`
}

startImaginaryServer({ welcome })
// client.ts

const greeting = await fetch(
  `https://aleksandra.says/rpc/welcome`,
  { body: JSON.stringify(1) }
)
// client.ts

import type { Server } from './server.ts'

const rpc = setupImaginaryClient<Server>()

const greeting = await rpc.welcome({ input: 1 })
// server.ts

import db from "prisma"

async function welcome(userId: string) {
  const user = await db.users.findFirst({ where: { id: userId }})
    
  return `Welcome again, ${user.name}!`
}

const server = startImaginaryServer({ welcome })
export type Server = typeof server

Let's see some code!

https://blitzjs.com

https://trpc.io/

Β 

  • Client & server tightly coupled
  • Having to use the same language
  • Need to learn all the procedure names
  • Having to use multi-threaded servers
  • Parameters marshalling
  • Exception handling

Problems (?) with RPC

Takeaways

Evolution & Iteration

We keep revisiting ideas from the past and improving on them.

Sometimes what you need today isn't the new, but a spin off of something old

1

Follow your needs

Keep in mind the needs of your software. Newer doesn't mean better. Pay attention to the hype to know what's up; don't toss away solutions that work for you.

2

REST is not dead, but it's not your only choice

It's always worth knowing all your options. RPC in a JavaScript world can be a good fit!

3

γ€ž

RPC is a natural fit for the increasingly ubiquitous practice of full-stack development with frameworks such as Next.js.

– Telefunc docs

Β‘Muchas gracias!

@aleksandrasays

www.aleksandra.codes

@aleksandra@mas.to