require 'swagger_helper'
RSpec.describe 'Api::V1::Posts', type: :request do
path '/api/v1/posts' do
get 'Retrieves all posts' do
tags 'Posts'
produces 'application/json'
parameter name: :page, in: :query, type: :integer, required: false
parameter name: :per_page, in: :query, type: :integer, required: false
response '200', 'posts found' do
schema type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
excerpt: { type: :string },
author: {
type: :object,
properties: {
id: { type: :integer },
name: { type: :string }
}
}
}
}
run_test!
end
end
post 'Creates a post' do
tags 'Posts'
consumes 'application/json'
produces 'application/json'
security [bearer_auth: []]
parameter name: :post, in: :body, schema: {
type: :object,
properties: {
title: { type: :string },
body: { type: :string },
tags: { type: :array, items: { type: :string } }
},
required: ['title', 'body']
}
response '201', 'post created' do
run_test!
end
response '422', 'invalid request' do
run_test!
end
end
end
end
Auto-generated API documentation from code annotations keeps docs in sync with implementation and reduces maintenance burden. The rswag gem generates OpenAPI 3.0 specs from RSpec request specs, providing interactive documentation via Swagger UI. I write request specs that describe expected inputs, outputs, and status codes, and rswag generates the corresponding OpenAPI schema. This approach serves triple duty: automated tests, documentation, and client SDK generation. The generated docs include example requests/responses, authentication requirements, and validation rules. I host Swagger UI at /api-docs in staging environments so frontend developers can explore endpoints interactively. The key is maintaining discipline around keeping specs comprehensive and up-to-date.
Related snips
class CommentsController < ApplicationController
before_action :set_post
def create
@comment = @post.comments.build(comment_params)
System test: asserting Turbo Stream responses
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
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
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
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
Share this code
Here's the card — post it anywhere.