Aleksandra Sikora, @aleksandrasays
- open-source engineer @
- 🧗♂️
- org of Wrocław
previously
-         .js Maintainer
-  cc           
🐦 @aleksandrasays
🐙 @beerose
🐘 @aleksandra@mas.to
🌎 https://aleksandra.codes
Paris Fashion Week Feb '23
Elsa Hosk, 2023, Cannes
🥴
😭
Boilerplate
Lost typesafety
Repetitive error handling
🤔
Fullstack TypeScript app
TYPE-SAFE
TYPE-SAFE
// one-computer.js
function welcome(name) {
  return `Hello, ${name}!`
}
const greeting = welcome("Helsinki!")
//    ^ "Hello, Helsinki!"
// server.js
function welcome(name) {
  return `Hello, ${name}!`
}
startImaginaryServer({ welcome })
// client.js
const greeting = await fetch(
  `https://aleksandra.says/rpc/welcome`,
  { body: JSON.stringify("Helsinki") }
)😌
🥴
now we're looking for sth
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;
  };
};
Developer trying to learn Corba
now we need
<?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>now we're looking for sth
When the web started to change
Can request and update resources
Exposes resources
| 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 | 
JSON-RPC
RESTful
"REST"
time for something
API
App
GET users/
GET tasks/
GET tags/
API
App
POST graphql/
Body:
{ "query": "query { users {...} }" }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
at least until stuff like Max Stoiber's GraphQL CDN popped up
for free and out of the box
Revisiting the original promise of RPC
?
Source: https://www.vice.com/en/article/m7azaa/best-baggy-oversized-clothes
Fullstack TypeScript app
Fullstack TypeScript app
Fullstack TypeScript app
tRPC query & mutation procedures
Remix loader pattern
React Server Components
Qwik City
Blitz RPC query & mutation resolvers
pRPC
// src/queries/getProject.ts (runs on the server)
import db from "db"
import * as z from "zod"
const GetProject = z.object({
  id: z.number(),
})
export default async function getProject(
  input: z.infer<typeof GetProject>
) {
  // Validate the input
  const data = GetProject.parse(input)
  const project = await db.project.findOne({ where: { id: data.id } })
  // Can do any processing, fetching from other APIs, etc
  return project
}// src/pages/index.tsx (runs on the client & server)
import { useQuery } from "@blitzjs/rpc"
import getProject from "src/projects/queries/getProject"
function Index() {
  const [project] = useQuery(getProject, { where: { id: 1 } })
  
  return <span>{project.id}</span>
}// src/pages/index.tsx (runs on the client & server)
import { useQuery } from "@blitzjs/rpc"
import getProject from "src/projects/queries/getProject"
function App() {
  const [project] = useQuery(getProject, { where: { id: 1 } })
  
  return <span>{project.id}</span>
}export const trpc = createTRPCNext<AppRouter>({ /* ... */});
// ---
function ProjectForm(props) {
  const updateProjectMutation = trpc.createProject.useMutation()
  return (
    <form
      onSubmit={values => {
        try {
          const project = mutation.mutate(values)
        } catch (error) {
          alert('Error saving project')
        }
      }}>
      {/* ... */}
    </form>
  )
}- @builder.io/qwik-city
- @tanstack/bling
import { useQuery } from '@tanstack/react-query';
import { server$ } from '@tanstack/bling';
  
export default function Index() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['project'],
    queryFn: server$(async () => { // 🤯
      return await db.project.findOne({ where: { id: input.id } });
    })
  })
    
  if (isLoading) return <span>Loading...</span>;
  if (error) throw error;
    
  return <span>{data.id}</span>;
}// components/Counter.tsx 
'use client'; // 👀
import { useState } from 'react';
export function Counter() {
  const [state, setState] = useState(0)
  return <button onClick={() => setState(s => s + 1)}>increment: {state}</button>
}
// app/page.tsx
import { db } from '../db';
import { Counter } from '../components/Counter.tsx';
export default function Page() {
  const posts = await db.posts.select('name', 'slug');
  return (
    <div>
      <Counter />
      <ul>
        {posts.map(post => (
          <li key={post.slug}>
            <a href={`/blog/${post.slug}`}>{post.name}</a>
          </li>
        ))}
      </ul>
    </div>
  )
}Source: https://twitter.com/markdalgleish/status/1256800146118959109
Chloé, Spring 2023
2001
😌 Type-safety out of the box
🚀 No runtime overhead
🔎 IDE features
?
Source: http://www.ashleyedavidson.com/blog/the-history-of-fashion-diffusion-in-pictures
Know your options
Take what fits you
Tailor to your needs
@aleksandrasays
www.aleksandra.codes