Open4

sqlc (go) 利用時に ts の型情報を生成してほしい

voluntasvoluntas

モチベーション

  • sqlc を使うと SQL で書いた内容を API の JSON として返してしまう場合が多い
    • emit_json_tags を true にする前提
    • TODO: omitempty- の機能が欲しい
  • ブラウザ -> Cloudflare Workers (TS) -> Go という構成だと、そのまま返して特に困らない
    • BFF (Backend for Frontend) として Cloudflare Workers を利用する場合は SQL からの出力そのまま出すことが多い
  • Buf/Connect 調べたが自分には合わなかった

何が嬉しくなるか

  • Go で書かれた API を呼ぶ側、つまり TypeScript 側で型情報が利用できるようになる

課題

  • バリデーションが未解決
  • ReqParams 部分は SQL に直接渡せるものもあれば、渡せないモノもあるので微妙
voluntasvoluntas

出力イメージ

sqlc generate したタイミングで go とは別に ts の型情報を出力する。

type GetProjectFromIdParams struct {
	ID string `json:"id"`
}

type GetProjectFromIdRow struct {
	Pk                             int64        `json:"pk"`
	ID                             string       `json:"id"`
	OrgPk                          int32        `json:"org_pk"`
	OrgID                          uuid.UUID    `json:"org_id"`
	DisplayName                    string       `json:"display_name"`
	Disabled                       bool         `json:"disabled"`
	Deleted                        bool         `json:"deleted"`
	CreatedAt                      time.Time    `json:"created_at"`
}

という struct がある場合、これらの TS の型をそのまま定義して欲しい。

type GetProjectFromIdParams = {
    id: string
}

type GetProjectFromIdRow = {
    pk: number
    id: string
    orgPk number
    orgId: string
    displayName: string
    disabled: boolean
    deleted: boolean
    createdAt: string
}
  • plugin では types の出力場所を指定できるようにして欲しい
  • types.d.ts に全部入ってしまえば良さそう
voluntasvoluntas

利用イメージ

  • type.d.ts をインポートして利用する。typescript の types に指定して利用するイメージか
  • イメージはシンプルなワーカー実装にしているが実際は Remix などのフレームワークを利用している
  • fetch で API を取ってきて、色々処理してから、React でレンダリングする
// from は適当
import type {
  GetProjectFromIdParams,
  GetProjectFromIdRow
} from '~/gen/sqlc/types'
import type {
  Request as WorkerRequest,
  ExecutionContext,
} from '@cloudflare/workers-types/experimental'

export default {
  async fetch(request: WorkerRequest, env: Env, context: ExecutionContext): Promise<Response> {
    return handleRequest(request, env)
  },
}

const handleRequest = async (request: WorkerRequest, env: Env): Promise<Response> => {
  const reqParams: GetProjectFromIdParams = { id: 'spam' }
  const res = await fetch('/api/get-project-from-id', {
    method: 'POST',
    body: JSON.stringify(reqParams),
  })
  const { id, orgId } = await res.json<GetProjectFromIdRow>()
  return new Response(JSON.stringify({ id, orgId }), { status: 200 })
}
voluntasvoluntas

sqlc-rpc

かなり雑な妄想

export const getProjectFromId = async (reqParams: GetProjectFromIdParams): Promise<GetProjectFromIdRow> => {
  const resp = await fetch("/rpc", {method: "POST", headers: {
    "content-type": "application/json",
    "x-sqlc-target": "SpamRPC_20230814.GetProjectFromId"
  }, body: JSON.stringify(reqParams)})
  return await resp.json<GetProjectFromIdRow>()
}
const { id, orgId } = await rpc.getProjectFromId({id: 'spam'})