← スキル一覧に戻る

api-endpoint
by GangWooLee
⭐ 0🍴 0📅 2026年1月25日
SKILL.md
name: api-endpoint description: Generate JSON API endpoints with authentication and versioning. Use when user needs API, mobile backend, external integration, or says "create API", "add endpoint", "JSON response", "API for mobile", "REST API".
API Endpoint Generator
Generate RESTful JSON API endpoints with authentication, versioning, and documentation.
Quick Start
Task Progress (copy and check off):
- [ ] 1. Design API endpoint structure
- [ ] 2. Create API controller
- [ ] 3. Add routes with versioning
- [ ] 4. Implement authentication
- [ ] 5. Add serialization
- [ ] 6. Handle errors
- [ ] 7. Test with curl/Postman
- [ ] 8. Document endpoints
API Structure
app/controllers/api/
├── v1/
│ ├── base_controller.rb
│ ├── posts_controller.rb
│ ├── users_controller.rb
│ └── sessions_controller.rb
└── v2/
└── ...
Base Controller
# app/controllers/api/v1/base_controller.rb
module Api
module V1
class BaseController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authenticate_api_user
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
private
def authenticate_api_user
token = request.headers["Authorization"]&.split(" ")&.last
@current_api_user = User.find_by(api_token: token)
unless @current_api_user
render json: { error: "Unauthorized" }, status: :unauthorized
end
end
def current_api_user
@current_api_user
end
def not_found
render json: { error: "Resource not found" }, status: :not_found
end
def unprocessable_entity(exception)
render json: { errors: exception.record.errors.full_messages }, status: :unprocessable_entity
end
end
end
end
Resource Controller
# app/controllers/api/v1/posts_controller.rb
module Api
module V1
class PostsController < BaseController
before_action :set_post, only: [:show, :update, :destroy]
# GET /api/v1/posts
def index
@posts = Post.includes(:user).order(created_at: :desc).limit(50)
render json: {
data: @posts.map { |post| post_json(post) },
meta: {
total: @posts.count,
page: 1,
per_page: 50
}
}
end
# GET /api/v1/posts/:id
def show
render json: { data: post_json(@post) }
end
# POST /api/v1/posts
def create
@post = current_api_user.posts.build(post_params)
if @post.save
render json: { data: post_json(@post) }, status: :created
else
render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
end
end
# PATCH /api/v1/posts/:id
def update
if @post.user != current_api_user
return render json: { error: "Forbidden" }, status: :forbidden
end
if @post.update(post_params)
render json: { data: post_json(@post) }
else
render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
end
end
# DELETE /api/v1/posts/:id
def destroy
if @post.user != current_api_user
return render json: { error: "Forbidden" }, status: :forbidden
end
@post.destroy
head :no_content
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :status)
end
def post_json(post)
{
id: post.id,
title: post.title,
content: post.content,
status: post.status,
likes_count: post.likes_count,
comments_count: post.comments_count,
created_at: post.created_at.iso8601,
updated_at: post.updated_at.iso8601,
user: {
id: post.user.id,
name: post.user.name,
avatar_url: post.user.avatar_url
}
}
end
end
end
end
Routes
# config/routes.rb
namespace :api do
namespace :v1 do
resources :posts, only: [:index, :show, :create, :update, :destroy]
resources :users, only: [:show, :create, :update]
post "sessions", to: "sessions#create"
delete "sessions", to: "sessions#destroy"
end
end
Authentication
Token-Based
Generate Token:
# app/models/user.rb
class User < ApplicationRecord
has_secure_token :api_token
def regenerate_api_token
regenerate_api_token
end
end
Migration:
add_column :users, :api_token, :string
add_index :users, :api_token, unique: true
Login Endpoint:
# app/controllers/api/v1/sessions_controller.rb
module Api
module V1
class SessionsController < BaseController
skip_before_action :authenticate_api_user, only: [:create]
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
render json: {
data: {
token: user.api_token,
user: user_json(user)
}
}, status: :created
else
render json: { error: "Invalid credentials" }, status: :unauthorized
end
end
def destroy
current_api_user.regenerate_api_token
head :no_content
end
private
def user_json(user)
{
id: user.id,
email: user.email,
name: user.name
}
end
end
end
end
Client Usage:
# Login
curl -X POST http://localhost:3000/api/v1/sessions \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password"}'
# Use token
curl http://localhost:3000/api/v1/posts \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
Response Format
Success (200/201)
{
"data": {
"id": 1,
"title": "Post Title",
"content": "Content here"
}
}
List (200)
{
"data": [
{ "id": 1, "title": "Post 1" },
{ "id": 2, "title": "Post 2" }
],
"meta": {
"total": 100,
"page": 1,
"per_page": 50
}
}
Error (4xx/5xx)
{
"error": "Resource not found"
}
Validation Error (422)
{
"errors": [
"Title can't be blank",
"Content is too short"
]
}
Pagination
def index
page = params[:page]&.to_i || 1
per_page = params[:per_page]&.to_i || 20
per_page = 100 if per_page > 100 # Max limit
@posts = Post.order(created_at: :desc)
.limit(per_page)
.offset((page - 1) * per_page)
render json: {
data: @posts.map { |post| post_json(post) },
meta: {
total: Post.count,
page: page,
per_page: per_page,
total_pages: (Post.count.to_f / per_page).ceil
}
}
end
Serialization (Optional)
Using Jbuilder:
# app/views/api/v1/posts/index.json.jbuilder
json.data @posts do |post|
json.id post.id
json.title post.title
json.content post.content
json.user do
json.id post.user.id
json.name post.user.name
end
end
json.meta do
json.total @posts.count
end
Controller:
def index
@posts = Post.includes(:user).limit(50)
# Renders app/views/api/v1/posts/index.json.jbuilder
end
CORS (for JavaScript clients)
Gemfile:
gem 'rack-cors'
config/initializers/cors.rb:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:3001', 'example.com' # Your frontend domains
resource '/api/*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
Testing
RSpec (optional):
# spec/requests/api/v1/posts_spec.rb
RSpec.describe "Api::V1::Posts", type: :request do
let(:user) { create(:user) }
let(:headers) { { "Authorization" => "Bearer #{user.api_token}" } }
describe "GET /api/v1/posts" do
it "returns posts" do
create_list(:post, 3)
get "/api/v1/posts", headers: headers
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body)["data"].count).to eq(3)
end
end
describe "POST /api/v1/posts" do
it "creates a post" do
post_params = { title: "New Post", content: "Content here" }
post "/api/v1/posts", params: { post: post_params }, headers: headers
expect(response).to have_http_status(:created)
expect(JSON.parse(response.body)["data"]["title"]).to eq("New Post")
end
end
end
Documentation
README section:
## API Documentation
### Authentication
All API requests require authentication via Bearer token.
```bash
Authorization: Bearer YOUR_TOKEN_HERE
Endpoints
GET /api/v1/posts
- Returns list of posts
- Params:
page,per_page
POST /api/v1/posts
- Create new post
- Body:
{ "post": { "title": "...", "content": "..." } }
GET /api/v1/posts/:id
- Returns single post
PATCH /api/v1/posts/:id
- Update post (owner only)
DELETE /api/v1/posts/:id
- Delete post (owner only)
## Best Practices
1. **Versioning**: Always use `/api/v1/` namespace
2. **Authentication**: Token in `Authorization` header
3. **Consistent Format**: `{ data: {}, meta: {} }`
4. **Error Handling**: Proper HTTP status codes
5. **N+1 Prevention**: Use `includes()` in queries
6. **Rate Limiting**: Consider adding (e.g., rack-attack gem)
7. **HTTPS Only**: In production
## Checklist
- [ ] API controller created in `app/controllers/api/v1/`
- [ ] Inherits from BaseController
- [ ] Routes added with `namespace :api, :v1`
- [ ] Authentication implemented
- [ ] JSON responses with `data` and `meta`
- [ ] Error handling for 404, 422, 401, 403
- [ ] CORS configured if needed
- [ ] Tested with curl or Postman
- [ ] API documented
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です