typescript 43 lines · 1 tab

TypeScript types from Rails serializers

Maya Patel Jan 2026
1 tab
export type PostId = string & { readonly brand: unique symbol }
export type UserId = string & { readonly brand: unique symbol }

export interface User {
  id: UserId
  name: string
  email: string
  avatar_url: string | null
}

export type PostStatus = 'draft' | 'published' | 'archived'

export interface Post {
  id: PostId
  title: string
  body: string
  excerpt: string
  status: PostStatus
  published_at: string | null
  created_at: string
  updated_at: string
  author: User
  tags: string[]
  comments_count: number
  likes_count: number
}

export interface PostFormData {
  title: string
  body: string
  status: PostStatus
  tags: string[]
}

export interface PaginatedResponse<T> {
  data: T[]
  meta: {
    current_page: number
    total_pages: number
    total_count: number
    per_page: number
  }
}
1 file · typescript Explain with highlit

Keeping TypeScript types in sync with Rails API responses is critical but tedious. I generate TypeScript interfaces automatically from Rails serializers or JSON Schema using tools like quicktype or custom scripts. For manual definitions, I create a types directory that mirrors the Rails model structure. Each type includes all attributes from the serializer plus computed fields. I use branded types for IDs to prevent mixing up user IDs with post IDs. Unions and literal types represent Rails enums. The key discipline is updating types whenever the API shape changes—I catch this in code review and with integration tests that verify response shapes. Proper types catch API contract violations at compile time instead of runtime.


Related snips

ruby
class CommentsController < ApplicationController
  before_action :set_post

  def create
    @comment = @post.comments.build(comment_params)

System test: asserting Turbo Stream responses

rails hotwire turbo
by codesnips 4 tabs
ruby
class Post < ApplicationRecord
  belongs_to :author, class_name: 'User'
  has_many :comments, dependent: :destroy

  scope :published, -> { where.not(published_at: nil).where('published_at <= ?', Time.current) }
  scope :draft, -> { where(published_at: nil) }

ActiveRecord scopes for reusable query logic

rails activerecord patterns
by Alex Kumar 1 tab
typescript
export interface RetryOptions {
  retries: number;
  baseMs: number;
  maxMs: number;
  signal?: AbortSignal;
  onRetry?: (attempt: number, delay: number, err: unknown) => void;

Exponential backoff with jitter for retries

typescript reliability retry
by codesnips 2 tabs
ruby
module Api
  module V1
    class UsersController < BaseController
      def show
        user = User.includes(:profile).find(params[:id])

ETags for conditional requests and caching

rails caching http-caching
by Alex Kumar 1 tab
ruby
class PostsController < ApplicationController
  def index
    @posts = Post.includes(:author)
                 .order(created_at: :desc)
                 .page(params[:page])
                 .per(10)

Turbo Frames: infinite scroll with lazy-loading frame

rails turbo hotwire
by codesnips 4 tabs
ruby
require "csv"

class PeopleCsvStream
  include Enumerable

  HEADERS = %w[id full_name email signed_up_at plan].freeze

Resilient CSV Export as a Streamed Response

rails performance streaming
by codesnips 3 tabs

Share this code

Here's the card — post it anywhere.

TypeScript types from Rails serializers — share card
Link copied