Samen 소개: TypeScript 개발자를 위한 종단간 typesafe API(GraphQL 필요 없음)

Facebook에서 GraphQL을 소개했을 때 우리는 흥분했습니다. Facebook은 많은 고품질 프로젝트를 생성합니다. 흥미롭고 유용해야 했겠죠? 음, 저는 GraphQL이 확실히 용도가 있다고 생각하지만 우리 중 99%는 실제로 필요하지 않다고 확신합니다.

GraphQL은 여러 언어에 대한 클라이언트를 생성할 때 빛을 발합니다. 또는 JS/TS 백엔드가 아닌 경우. 또한 GitHub, Spotify 또는 Shopify와 같은 공개 API의 경우 친숙한 인터페이스로 클라이언트를 쉽게 생성할 수 있는 도구가 있다는 것은 정말 멋진 일입니다.

그러나 우리와 같은 풀스택 TypeScript 개발자이고 서버와 클라이언트를 모두 생성해야 하는 경우 GraphQL은 그 사이에 있는 깔때기와 같습니다. 불필요한 팽창, 복잡성 및 혼란을 많이 추가합니다.

우리는 RPC 스타일의 API 프레임워크를 꿈꾸었고 그것을 구축하기로 결정했습니다.

사멘을 소개합니다



3단계로 어떻게 작동하는지 보여드리겠습니다.

텍스트보다 비디오를 선호하는 경우: 기사 하단으로 스크롤하십시오.

1단계: 백엔드 기능 노출



백엔드 프로젝트의 루트에 있는 samen.ts라는 파일만 있으면 됩니다. 이 파일에서 프런트엔드에서 호출해야 하는 기능을 내보냅니다. SamencreateService API를 사용하여 "서비스"라고 부르는 기능을 구성합니다. 이렇게:

import { createService } from "@samen/server"

async function sayHello(name: string): Promise<string> {
  return `Hello, ${name}`
}

async function sayGoodbye(name: string): Promise<string> {
  return `Goodbye, ${name}`
}

export const greetingService = createService({
  sayHello,
  sayGoodbye,
})


2단계: 동일한 실행



Samen은 코드를 감시, 컴파일 및 생성하기 위한 장기 실행 프로세스와 함께 제공됩니다. 프로젝트 디렉토리에서 npx samen를 실행하면 프런트엔드에 samen.generated.ts라는 파일이 생성됩니다. 이 파일에는 Samen 백엔드용 SDK가 포함되어 있습니다.

3단계: 백엔드 기능 호출



이제 백엔드에서 함수를 호출하는 데 사용할 수 있는 SamenClient 파일에서 samen.generated를 가져올 수 있습니다. 당신이 해야 할 유일한 일은 Samen에게 당신이 선택한 가져오기 라이브러리(Samen이 실제 HTTP 호출에 사용함)를 전달하는 것입니다.

import fetch from "isomorphic-unfetch"

import { SamenClient } from "../samen.generated"

// provide Samen your favourite fetch library
const samen = new SamenClient(fetch)

const helloMessage = await 
  samen.greetingService.sayHello(
    "Steve Jobs",
  )

console.log(helloMessage) // prints "Hello, Steve Jobs"

const goodbyeMessage = await 
  samen.greetingService.sayGoodbye(
    "Steve Wozniak",
  )

console.log(goodbyeMessage)  // prints "Goodbye, Steve Wozniak"



그게 전부입니다. 보시다시피 프런트엔드에서 함수를 호출하는 것과 거의 같습니다. 그것이 사멘의 마법입니다.

서버와 클라이언트 간 모델 공유



가장 좋은 점은 Samen이 노출된 백엔드 기능을 자동으로 분석하고 필요한 모든 모델을 SDK에 넣는 것입니다. 이제 원하는 것을 사용할 수 있습니다. 글쎄, 거의 모든 것. Samen은 인터페이스, 열거형 및 유형 별칭을 지원합니다. 수업을 지원하지 않습니다.

작동 방식은 다음과 같습니다.

import { createService } from "@samen/server"

interface Message {
  text: string
}

async function sayHello(name: string): Promise<Message> {
  return { text: `Hello, ${name}` }
}

async function sayGoodbye(name: string): Promise<Message> {
    return { text: `Goodbye, ${name}` }
}

export const greetingService = createService({
  sayHello,
  sayGoodbye,
})


이제 프런트엔드에서 Message 모델을 가져올 수 있습니다. 이와 같이:

import fetch from "isomorphic-unfetch"

import { SamenClient, Message } from "../samen.generated"

// provide Samen your favourite fetch lib
const samen = new SamenClient(fetch)

const helloMessage: Message = await 
  samen.greetingService.sayHello(
    "Steve Jobs",
  )

console.log(helloMessage) // prints { text: "Hello, Steve Jobs" }

const goodbyeMessage: Message = await 
  samen.greetingService.sayGoodbye(
    "Steve Wozniak",
  )

console.log(goodbyeMessage) // prints { text: "Goodbye, Steve Wozniak" }


TypeScript는 GraphQL보다 훨씬 표현력이 뛰어납니다. 관련된 중간 언어가 없기 때문에 TypeScript가 제공해야 하는 모델에서 원하는 모든 기능(제네릭, 조건부 유형, 인덱스 유형 등)을 사용할 수 있습니다. 데이터 모델을 100% 제어할 수 있습니다.

하지만 보여드리고 싶은 기능이 하나 더 있습니다.

오류 처리



API에서 중요하지만 간과되는 부분은 오류 처리입니다. 아시다시피 GraphQL 백엔드에서는 오류 처리가 정말 형편없습니다(일반 HTTP/REST에서는 그다지 좋지 않습니다). 우리는 Samen이 이것을 훨씬 더 잘 처리한다고 생각합니다.

매개변수화된 sayHello가 너무 짧을 때 name 함수가 오류를 발생시키길 원한다고 가정해 봅시다. 이와 같이:

class NameTooShortError extends Error {
}

async function sayHello(name: string): Promise<Message> {
  if (name.length < 3) {
    throw new NameTooShortError()
  }
  return {
    text: `Hello, ${name}`,
  }
}


클라이언트에서 오류를 처리하는 방법은 다음과 같습니다.

import { SamenClient, HelloMessage, NameTooShortError } from "../samen.generated"

// provide Samen your favourite fetch lib
const samen = new SamenClient(fetch)


try {
  const helloMessage: HelloMessage = await 
    samen.exampleService.sayHello(
      "", // oops!
    )
} catch (e) {
  if (e instanceof NameTooShortError) {
    alert("Name is too short!")
  }
}


보시다시피 이제 SDK에서 Error 클래스를 가져와서 처리할 수 있습니다.

더 좋아집니다. 오류에 속성을 넣을 수도 있습니다. 이와 같이:

class NameTooShortError extends Error {
  constructor(public readonly minimumLength: number) {
    super()
  }
}


프런트엔드에서 오류를 처리할 때 유용합니다.

} catch (e) {
  if (e instanceof NameTooShortError) {
    alert(`Name is too short, it should be at least ${e.minimumLength} characters!`)
  }
}


Samen은 middleware과 같이 더 많은 것을 제공합니다. 그러나 그것은 다른 게시물입니다.

동영상



Samen이 실제로 작동하는 모습을 보고 싶다면 제 친구 Jasper가 Samen의 작동 방식에 대해 설명하는 동영상이 있습니다.



우리는 당신의 의견을 듣고 싶습니다!



우리는 Samen을 출시하게 되어 매우 기쁩니다. TypeScript 개발자를 위한 API 개발에 대한 새로운 시도라고 생각합니다. 우리는 당신이 그것을 시도하고 우리는 당신의 의견을 희망합니다.

좋은 웹페이지 즐겨찾기