Goでのミドルウェア開発：Negroniの使い方

[![Group2156.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/f703cf74-1510-4647-a7b5-796183ca0785.png)](https://leapcell.io/?lc_t=q_negroni)


**[Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/?lc_t=q_negroni)**

# Negroni ライブラリの紹介
Negroni は、HTTP ミドルウェアに焦点を当てたライブラリです。そのサイズは小さく、非侵襲的であり、標準ライブラリ `net/http` のハンドラの使用を奨励しています。この記事では、このライブラリについて詳細に紹介します。

## I. なぜミドルウェアを使うのか
開発の過程で、統計、ロギング、デバッグなどの共通のロジックコードがあり、これらのロジックは各ハンドラで必要となる場合があります。これらのコードを各ハンドラに一つずつ追加すると、面倒ばかりでなく、エラーや見落としが発生しやすくなります。

ハンドラの実行にかかる時間を計測する例を挙げます。時間計測のコードを各ハンドラに追加する場合、以下のようになります：
```go
package main

import (
    "fmt"
    "net/http"
    "time"
)

func index(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    fmt.Fprintf(w, "leapcell page")
    fmt.Printf("index elasped:%fs", time.Since(start).Seconds())
}

func greeting(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    name := r.FormValue("name")
    if name == "" {
        name = "world"
    }
    fmt.Fprintf(w, "hello %s", name)
    fmt.Printf("greeting elasped:%fs\n", time.Since(start).Seconds())
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", index)
    mux.HandleFunc("/greeting", greeting)
    http.ListenAndServe(":8000", mux)
}
```
しかし、このアプローチには多くの欠点があります：
1. **柔軟性の欠如**：新しいハンドラを追加するたびに、この時間計測のコードを追加する必要があり、これらのコードは実際のハンドラロジックとは直接関係がありません。
2. **見落としやすい**：ハンドラを書くときに、このようなコードを忘れやすく、特にすべての可能な戻り経路を考慮するときには、コーディングの負荷が増えます。
3. **修正しにくい**：統計コードにエラーがある場合や、調整が必要な場合には、関係するすべてのハンドラを修正する必要があります。
4. **新しいロジックの追加が難しい**：他の統計ロジックを追加する必要がある場合、すべてのハンドラのコードも変更する必要があります。

Go 言語のクロージャ機能を利用することで、実際のハンドラコードを関数にカプセル化し、この関数内で追加のロジックを実行することができます。以下に示します：
```go
func elasped(h func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path
        start := time.Now()
        h(w, r)
        fmt.Printf("path:%s elasped:%fs\n", path, time.Since(start).Seconds())
    }
}

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "leapcell page")
}

func greeting(w http.ResponseWriter, r *http.Request) {
    name := r.FormValue("name")
    if name == "" {
        name = "world"
    }
    fmt.Fprintf(w, "hello %s", name)
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", elasped(index))
    mux.HandleFunc("/greeting", elasped(greeting))
    http.ListenAndServe(":8000", mux)
}
```
この例では、ハンドラと関係のない追加コードを `elasped` 関数に配置しています。ハンドラ関数を登録するときには、元のハンドラ関数を直接使用するのではなく、`elasped` 関数でカプセル化しています。実際、`elasped` のような関数はミドルウェアです。元のハンドラ関数をカプセル化し、新しいハンドラ関数を返します。これにより、実際の処理ロジックの前後にコードを挿入することができ、追加、修正、保守がしやすくなります。

## II. Negroni のクイックスタート
### (I) インストール
以下のコマンドを実行して、Negroni ライブラリをインストールします：
```bash
$ go get github.com/urfave/negroni
```
### (II) 使用例
```go
package main

import (
    "fmt"
    "net/http"
    "github.com/urfave/negroni"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.Classic()
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
Negroni は比較的使いやすく、`http.Handler` と簡単に連携することができます。`negroni.Classic()` はいくつかの一般的なミドルウェアを提供します：
1. **negroni.Recovery**：プログラムの実行中に発生する `panic` から復旧するために使用されます。ハンドラコードで `panic` が発生した場合、このミドルウェアは例外をキャッチし、プログラムの終了を防ぎます。
2. **negroni.Logger**：リクエストとレスポンスの基本情報を記録し、ロギング機能を実装します。
3. **negroni.Static**：`public` ディレクトリ内の静的ファイルサービスを提供することができます。

`n.UseHandler(mux)` を呼び出すことで、これらのミドルウェアがマルチプレクサ `mux` に適用されます。プログラムを実行した後、ブラウザに `localhost:3000` を入力すると、コンソールに以下のような出力が表示されます：
```bash
[negroni] 2025-03-22T18:48:53+08:00 | 200 |      10.9966ms | localhost:8080 | GET /
```

## III. Negroni の詳細な機能
### (I) negroni.Handler インターフェイス
`negroni.Handler` インターフェイスは、ミドルウェアの実行プロセスに対してより柔軟な制御を提供します。インターフェイスは以下のように定義されています：
```go
type Handler interface {
    ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
書かれたミドルウェアのシグネチャは `func(http.ResponseWriter, *http.Request, http.HandlerFunc)` でなければならず、または `negroni.Handler` インターフェイスを実装する必要があります。例えば：
```go
func RandomMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    if rand.Int31n(100) <= 50 {
        fmt.Fprintf(w, "hello from RandomMiddleware")
    } else {
        next(w, r)
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n.Use(negroni.HandlerFunc(RandomMiddleware))
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
上記のコードは、ランダムなミドルウェアを実装しています。`RandomMiddleware` ミドルウェアから直接レスポンスを返す確率は 50% で、実際のハンドラ関数を実行する確率も 50% です。プログラムを実行した後、ブラウザで `localhost:8080` を繰り返しリフレッシュして、その効果を観察してください。

実際、`func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)` は便利な書き方です。`n.Use` を呼び出すときに、`negroni.HandlerFunc` でカプセル化され、`negroni.HandlerFunc` は `negroni.Handler` インターフェイスを実装しています。以下は関連するコードです：
```go
// src/github.com/urfave/negroni/negroni.go
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    h(rw, r, next)
}
```
同様に、`net/http` では、`func(http.ResponseWriter, *http.Request)` も `http.HandlerFunc` でカプセル化されて、`http.Handler` インターフェイスを実装しています。

### (II) negroni.With メソッド
複数のミドルウェアがある場合、`n.Use()` メソッドを使って一つずつ追加するのは面倒です。Negroni は `With()` メソッドを提供しており、一つ以上の `negroni.Handler` パラメータを受け取り、新しいオブジェクトを返します。例えば：
```go
func Middleware1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    fmt.Println("Middleware A begin")
    next(w, r)
    fmt.Println("Middleware A end")
}

func Middleware2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    fmt.Println("Middleware B begin")
    next(w, r)
    fmt.Println("Middleware B end")
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n = n.With(
        negroni.HandlerFunc(Middleware1),
        negroni.HandlerFunc(Middleware2),
    )
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```

### (III) Run メソッド
Negroni オブジェクトは `Run()` メソッドを提供しており、サーバプログラムを簡単に実行するために使用されます。このメソッドは `http.ListenAndServe()` と同じアドレス (Addr) パラメータを受け取ります。例えば：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n.UseHandler(mux)
    n.Run(":8080")
}
```
ポートが指定されていない場合、`Run()` メソッドは `PORT` 環境変数を使用しようとします。`PORT` 環境変数も設定されていない場合、デフォルトのポート `:8080` が使用されます。

### (IV) http.Handler としての使用
Negroni は `net/http` プログラムで簡単に使用することができ、`negroni.Negroni` オブジェクトを直接 `http.Handler` として対応するメソッドに渡すことができます。例えば：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.Classic()
    n.UseHandler(mux)
    s := &http.Server{
        Addr:           ":8080",
        Handler:        n,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    s.ListenAndServe()
}
```

## IV. 組み込みミドルウェア
### (I) Static
`negroni.Static` ミドルウェアは、指定されたディレクトリ内のファイルサービスを提供することができます。例のコードは以下の通りです：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello world")
    })
    n := negroni.New()
    n.Use(negroni.NewStatic(http.Dir("./public")))
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
プログラムの実行ディレクトリ内に `public` ディレクトリを作成し、その中にいくつかのファイル (例えば `1.txt`, `2.jpg`) を配置してください。プログラムを実行した後、ブラウザで `localhost:8080/1.txt` や `localhost:8080/2.jpg`# Negroni ライブラリの紹介
Negroni は、HTTP ミドルウェアに焦点を当てたライブラリです。そのサイズは小さく、非侵襲的であり、標準ライブラリ `net/http` のハンドラの使用を奨励しています。この記事では、このライブラリについて詳細に紹介します。

## I. なぜミドルウェアを使うのか
開発の過程で、統計、ロギング、デバッグなどの共通のロジックコードがあり、これらのロジックは各ハンドラで必要となる場合があります。これらのコードを各ハンドラに一つずつ追加すると、面倒ばかりでなく、エラーや見落としが発生しやすくなります。

ハンドラの実行にかかる時間を計測する例を挙げます。時間計測のコードを各ハンドラに追加する場合、以下のようになります：
```go
package main

import (
    "fmt"
    "net/http"
    "time"
)

func index(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    fmt.Fprintf(w, "leapcell page")
    fmt.Printf("index elasped:%fs", time.Since(start).Seconds())
}

func greeting(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    name := r.FormValue("name")
    if name == "" {
        name = "world"
    }
    fmt.Fprintf(w, "hello %s", name)
    fmt.Printf("greeting elasped:%fs\n", time.Since(start).Seconds())
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", index)
    mux.HandleFunc("/greeting", greeting)
    http.ListenAndServe(":8000", mux)
}
```
しかし、このアプローチには多くの欠点があります：
1. **柔軟性の欠如**：新しいハンドラを追加するたびに、この時間計測のコードを追加する必要があり、これらのコードは実際のハンドラロジックとは直接関係がありません。
2. **見落としやすい**：ハンドラを書くときに、このようなコードを忘れやすく、特にすべての可能な戻り経路を考慮するときには、コーディングの負荷が増えます。
3. **修正しにくい**：統計コードにエラーがある場合や、調整が必要な場合には、関係するすべてのハンドラを修正する必要があります。
4. **新しいロジックの追加が難しい**：他の統計ロジックを追加する必要がある場合、すべてのハンドラのコードも変更する必要があります。

Go 言語のクロージャ機能を利用することで、実際のハンドラコードを関数にカプセル化し、この関数内で追加のロジックを実行することができます。以下に示します：
```go
func elasped(h func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path
        start := time.Now()
        h(w, r)
        fmt.Printf("path:%s elasped:%fs\n", path, time.Since(start).Seconds())
    }
}

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "leapcell page")
}

func greeting(w http.ResponseWriter, r *http.Request) {
    name := r.FormValue("name")
    if name == "" {
        name = "world"
    }
    fmt.Fprintf(w, "hello %s", name)
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", elasped(index))
    mux.HandleFunc("/greeting", elasped(greeting))
    http.ListenAndServe(":8000", mux)
}
```
この例では、ハンドラと関係のない追加コードを `elasped` 関数に配置しています。ハンドラ関数を登録するときには、元のハンドラ関数を直接使用するのではなく、`elasped` 関数でカプセル化しています。実際、`elasped` のような関数はミドルウェアです。元のハンドラ関数をカプセル化し、新しいハンドラ関数を返します。これにより、実際の処理ロジックの前後にコードを挿入することができ、追加、修正、保守がしやすくなります。

## II. Negroni のクイックスタート
### (I) インストール
以下のコマンドを実行して、Negroni ライブラリをインストールします：
```bash
$ go get github.com/urfave/negroni
```
### (II) 使用例
```go
package main

import (
    "fmt"
    "net/http"
    "github.com/urfave/negroni"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.Classic()
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
Negroni は比較的使いやすく、`http.Handler` と簡単に連携することができます。`negroni.Classic()` はいくつかの一般的なミドルウェアを提供します：
1. **negroni.Recovery**：プログラムの実行中に発生する `panic` から復旧するために使用されます。ハンドラコードで `panic` が発生した場合、このミドルウェアは例外をキャッチし、プログラムの終了を防ぎます。
2. **negroni.Logger**：リクエストとレスポンスの基本情報を記録し、ロギング機能を実装します。
3. **negroni.Static**：`public` ディレクトリ内の静的ファイルサービスを提供することができます。

`n.UseHandler(mux)` を呼び出すことで、これらのミドルウェアがマルチプレクサ `mux` に適用されます。プログラムを実行した後、ブラウザに `localhost:3000` を入力すると、コンソールに以下のような出力が表示されます：
```bash
[negroni] 2025-03-22T18:48:53+08:00 | 200 |      10.9966ms | localhost:8080 | GET /
```

## III. Negroni の詳細な機能
### (I) negroni.Handler インターフェイス
`negroni.Handler` インターフェイスは、ミドルウェアの実行プロセスに対してより柔軟な制御を提供します。インターフェイスは以下のように定義されています：
```go
type Handler interface {
    ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
```
書かれたミドルウェアのシグネチャは `func(http.ResponseWriter, *http.Request, http.HandlerFunc)` でなければならず、または `negroni.Handler` インターフェイスを実装する必要があります。例えば：
```go
func RandomMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    if rand.Int31n(100) <= 50 {
        fmt.Fprintf(w, "hello from RandomMiddleware")
    } else {
        next(w, r)
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n.Use(negroni.HandlerFunc(RandomMiddleware))
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
上記のコードは、ランダムなミドルウェアを実装しています。`RandomMiddleware` ミドルウェアから直接レスポンスを返す確率は 50% で、実際のハンドラ関数を実行する確率も 50% です。プログラムを実行した後、ブラウザで `localhost:8080` を繰り返しリフレッシュして、その効果を観察してください。

実際、`func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)` は便利な書き方です。`n.Use` を呼び出すときに、`negroni.HandlerFunc` でカプセル化され、`negroni.HandlerFunc` は `negroni.Handler` インターフェイスを実装しています。以下は関連するコードです：
```go
// src/github.com/urfave/negroni/negroni.go
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    h(rw, r, next)
}
```
同様に、`net/http` では、`func(http.ResponseWriter, *http.Request)` も `http.HandlerFunc` でカプセル化されて、`http.Handler` インターフェイスを実装しています。

### (II) negroni.With メソッド
複数のミドルウェアがある場合、`n.Use()` メソッドを使って一つずつ追加するのは面倒です。Negroni は `With()` メソッドを提供しており、一つ以上の `negroni.Handler` パラメータを受け取り、新しいオブジェクトを返します。例えば：
```go
func Middleware1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    fmt.Println("Middleware A begin")
    next(w, r)
    fmt.Println("Middleware A end")
}

func Middleware2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    fmt.Println("Middleware B begin")
    next(w, r)
    fmt.Println("Middleware B end")
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n = n.With(
        negroni.HandlerFunc(Middleware1),
        negroni.HandlerFunc(Middleware2),
    )
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```

### (III) Run メソッド
Negroni オブジェクトは `Run()` メソッドを提供しており、サーバプログラムを簡単に実行するために使用されます。このメソッドは `http.ListenAndServe()` と同じアドレス (Addr) パラメータを受け取ります。例えば：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.New()
    n.UseHandler(mux)
    n.Run(":8080")
}
```
ポートが指定されていない場合、`Run()` メソッドは `PORT` 環境変数を使用しようとします。`PORT` 環境変数も設定されていない場合、デフォルトのポート `:8080` が使用されます。

### (IV) http.Handler としての使用
Negroni は `net/http` プログラムで簡単に使用することができ、`negroni.Negroni` オブジェクトを直接 `http.Handler` として対応するメソッドに渡すことができます。例えば：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
    })
    n := negroni.Classic()
    n.UseHandler(mux)
    s := &http.Server{
        Addr:           ":8080",
        Handler:        n,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    s.ListenAndServe()
}
```

## IV. 組み込みミドルウェア
### (I) Static
`negroni.Static` ミドルウェアは、指定されたディレクトリ内のファイルサービスを提供することができます。例のコードは以下の通りです：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello world")
    })
    n := negroni.New()
    n.Use(negroni.NewStatic(http.Dir("./public")))
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
プログラムの実行ディレクトリ内に `public` ディレクトリを作成し、その中にいくつかのファイル (例えば `1.txt`, `2.jpg`) を配置してください。プログラムを実行した後、ブラウザで `localhost:8080/1.txt` や `localhost:8080/2.jpg`をアクセスすることで、これらのファイルをリクエストすることができます。特に注意すべきは、対応するファイルが見つからない場合、`Static` ミドルウェアはリクエストを次のミドルウェアまたはハンドラ関数に渡すことです。例えば、ブラウザで `localhost:3000/none - exist.txt` を入力すると、`hello world` のレスポンスが表示されます。

### (II) Logger
「クイックスタート」のセクションでは、`negroni.Classic()` を通じてこのミドルウェアを使用しました。また、単独で使用してリクエスト情報を記録することもでき、`SetFormat()` メソッドを呼び出してログ形式を設定することができます。例えば：
```go
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "hello world")
    })
    n := negroni.New()
    logger := negroni.NewLogger()
    logger.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")
    n.Use(logger)
    n.UseHandler(mux)
    http.ListenAndServe(":8080", n)
}
```
上記のコードは、ログ形式を `[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}` に設定しており、すなわちレスポンスステータス、実行時間、`UserAgent` を記録します。

## V. サードパーティのミドルウェア
組み込みのミドルウェアに加えて、Negroni には多数のサードパーティのミドルウェアも存在します。完全なリストについては、[https://github.com/urfave/negroni?tab=readme-ov-file#third-party-middleware](https://github.com/urfave/negroni?tab=readme-ov-file#third-party-middleware) を参照してください。

## VI. まとめ
Negroni はミドルウェア機能に焦点を当てており、あまり面倒でまれにしか使われない派手な機能はありません。その非侵襲的な設計の特徴により、標準ライブラリ `net/http` や他の Web ライブラリ（例えば `gorilla/mux`）とシームレスに連携することができます。この特徴は、開発者が HTTP サービスを構築する際に大きな利便性をもたらします。開発者は実際のニーズに応じて柔軟にミドルウェアを選択し、組み合わせることで、ロギング、エラーハンドリング、リクエスト統計などの様々な機能を実現することができ、既存のコードアーキテクチャを大規模に変更する必要はありません。

全体として、Negroni は Go 言語の HTTP サービス開発において非常に価値のあるライブラリです。効率的で、柔軟で、保守しやすい Web アプリケーション開発を追求する人にとって、Negroni は間違いなく深く理解し、幅広く適用する価値のある優れたツールです。

# [Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/?lc_t=q_negroni)
最後に、Go サービスをデプロイするための最高のプラットフォーム：**[Leapcell](https://leapcell.io/?lc_t=q_negroni)** を紹介します。

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/2098f44e-50d1-4fd7-9a98-60157d48e962.png)](https://leapcell.io/?lc_t=q_negroni)


### 🚀 好きな言語で構築する
JavaScript、Python、Go、または Rust で簡単に開発できます。

### 🌍 無料で無制限のプロジェクトをデプロイする
使用する分だけ支払います — リクエストがなければ、請求もありません。

### ⚡ 使った分だけ支払い、隠された費用はありません
アイドル料はなく、シームレスなスケーラビリティが保証されます。

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/205027c2-8295-49e0-b0a2-9fe45c9647e4.png)](https://leapcell.io/?lc_t=q_negroni)


[📖 ドキュメントを参照する](https://docs.leapcell.io/)  

🔹 Twitter でフォローしてください：[@LeapcellHQ](https://x.com/LeapcellHQ)