LoginSignup
148
145

More than 5 years have passed since last update.

モダンな4つの言語で簡単なAPI作ってみた

Last updated at Posted at 2019-04-09

タイトル詐欺です
僕が超偏見と独断で最近キテる or 流行りそうな予感な言語4つを選びました✋

使用言語

  • Python
  • Nim
  • Go
  • Kotlin

概要

今回は独断と偏見で4つの言語(Python, Nim, Go, Kotlin)の軽量フレームワーク(順にflask,jester,echo,spark)を用いて簡単なAPIを作成し、その手軽さを比較してみました

環境

Python 3.6.4
Nim 0.19.2
Go 1.11.4
Kotlin 1.3.21
MySQL 5.7.25
MySQLクライアント
Python: PyMySQL
Nim: db_mysql
Go: go-sql-driver/mysql
Kotlin: kotlin.jdbc

今回はなんとなくORMではなく純粋なMySQLクライアントを使用しました

注意

ド素人エンジニアなのでところどころ間違ったことを記している可能性があります.遠慮なくご指摘いただけるとありがたいです!

データの用意

簡単なデータ処理を行えるように以下のようなデータを用意しました

tasksテーブル

id name created_at
1 aaaa 2019-04-06 23:40:56

Python(flask)

Flaskを用いて簡単なAPIを作成します

手軽さ

★★★★★
3分くらいでAPI叩けるようになります

始め方

pythonが入っている前提でお話します

まずはflaskをインストールします

pip install flask

適当なファイルを作成します

mkdir flask_sample
cd flask_sample
touch server.py

ルーティングを定義します

server.py
from flask import Flask
app = Flask(__name__)


@app.route("/")
def hello():
    return "Hello World!"


if __name__ == '__main__':
    app.debug = True
    app.run(host='0.0.0.0')
python server.py

デフォルトが5000番ポートみたいなのでアクセスすればブラウザにHelloWorldが表示されます
http://127.0.0.1:5000/

DB操作

PyMySQLを使用してデータベースに接続し操作を行います
ルーティングをtasks/[taskId]にして指定したIDのデータを取得して返します

pip install PyMySQL

server.py を以下のように変更

server.py
from flask import Flask, jsonify
import pymysql.cursors
app = Flask(__name__)


def getDBConnection():
    return pymysql.connect(
        host='host',
        port=port, # 文字列ではなくint
        user='user',
        passwd='pass',
        db='db',
        cursorclass=pymysql.cursors.DictCursor
    )

@app.route("/tasks/<int:taskId>")
def hello(taskId):
    task = getTask(taskId)
    return jsonify(task)


def getTask(taskId):
    conn = getDBConnection()
    try:
        with conn.cursor() as cursor:
            # Read a single record
            sql = "SELECT * FROM `tasks` WHERE id = %s"
            cursor.execute(sql, (taskId,))
            result = cursor.fetchone()
            return result
    finally:
        conn.close()
        return result


if __name__ == '__main__':
    app.debug = True
    app.run(host='0.0.0.0') # 127.0.0.1 だとpostmanから叩けません多分…

http://localhost:5000/tasks/1
でjsonが帰ってくるはずです

Nim(jester)

Nimはpythonライクに書けるコンパイラ言語です
処理速度がめちゃ早いらしい
Nim言語感想&概説
至高の言語、Nimを始めるエンジニアへ
jester を使います

手軽さ

★★★☆☆
こちらも5分くらいでAPIサーバーが立ちます
がNimの手軽さを考慮して★3つ

始め方

webフレームワークjesterのインストール

nimble install jester

適当なファイルを作成

touch server.nim

中身

server.nim
import jester

routes:
  get "/":
    resp "Hello world!"

実行

nim c -r server.nim  

http://localhost.5000
Hello world!が表示されます!これまた早い!

DB操作

db_mysqlを使用してデータベースに接続し操作を行います
ルーティングをtasks/[taskId]にして指定したIDのデータを取得して返します

Nimは型定義もできますが、varを使用すれば型推論してくれます

server.nim
import jester, random, strutils, asyncdispatch, json, db_mysql, times

proc getDBConnection(): DbConn =
    open("host:port", "user", "pass", "db")


proc getTask(taskId: int): Row =
    var dbConn: DbConn = getDBConnection()
    var task: Row = dbConn.getRow(sql "SELECT * FROM tasks WHERE id = " & $taskId)
    result = task # resultが返り値になります

routes:
    get "/tasks/@id":
        var task: Row = getTask(parseInt(@"id"))
        var data = %*{"id": task[0], "name": task[1], "created_at": task[2]}
        resp $data, "application/json"

http://localhost:5000/tasks/1
でjsonが帰ってくるはずです

Go(echo)

入門手軽さ

★★★★☆
こちらも超軽量フレームワークなので一瞬で立てれます
goのプロジェクト作成とかとか少し面倒?なので4つ

始め方

僕はgvmでgoのバージョンを管理しているのでバージョンを指定してGOPATHに入ります

gvm use 1.11.4
cd $GOPATH/src
mkdir go_api
cd go_api

今回はechoを使用します

go get -u github.com/labstack/echo/...

ファイルを作成

touch server.go
server.go
package main

import (
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

実行します

go run main.go server.go                                                                                  21:26:02

  ____    __
 / __/___/ /  ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.0.0
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                   O\
⇨ http server started on [::]:1323

http://localhost:1323
でHello, World!が表示されます

DB操作

今回はgo-sql-driver/mysqlを使用します
goはデータを構造体で定義してそこにマッピングしていくことでができます
構造体を定義する際にjsonで返す時のキーも指定できます

go get github.com/go-sql-driver/mysql
main.go
package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "strconv"
    "time"

    _ "github.com/go-sql-driver/mysql"
    "github.com/labstack/echo"
)

type Task struct {
    ID        int        `json:"id"`
    Name      string     `json:"name"`
    CreatedAt *time.Time `json:"created_at"`
}

func main() {
    e := echo.New()
    e.GET("/tasks/:id", getTask)
    e.Logger.Fatal(e.Start(":1323"))
}

func getDBConnection() *sql.DB {
    db, err := sql.Open("mysql", "username:pasword@tcp(host:port)/dbname?parseTime=true") //parseTime=trueがないとtime.Timeがうまくparseできない
    if err != nil {
        panic(err.Error())
    }
    return db
}

func getTask(c echo.Context) error {
    id, _ := strconv.Atoi(c.Param("id")) // c.Param("id")でクエリパラメータ取得
    task := selectTask(id)
    return c.JSON(http.StatusOK, task) // 200とjsonを返す
}

func selectTask(id int) Task {
    db := getDBConnection() // db接続
    defer db.Close() // 最後にCloseする宣言
    task := Task{} // 構造体の初期化
  // select文の発行と構造体のそれぞれのメンバーにマッピング
    if err := db.QueryRow("SELECT * FROM tasks WHERE id = ?", id).Scan(&task.ID, &task.Name, &task.CreatedAt); err != nil {
        log.Fatal(err)
    }
    return task
}

http://localhost:1323/tasks/1
でjsonが帰ってくるはずです

Kotlin(spark)

入門手軽さ

★☆☆☆☆
gradle+sparkでやってますが、HelloWorldを表示させるのも一苦労でした
IDEに頼らずコマンドライン縛りでやったら
2〜3時間かかりました…
が、以下コピペで10分くらいで立ち上げれます
mavenに挫折してgradleにしました

始め方

gradleプロジェクトを作成します
使用バージョンは以下です

$ gradle -v

------------------------------------------------------------
Gradle 5.3.1
------------------------------------------------------------

Build time:   2019-03-28 09:09:23 UTC
Revision:     f2fae6ba563cfb772c8bc35d31e43c59a5b620c3

Kotlin:       1.3.21
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM:          1.8.0_161 (Oracle Corporation 25.161-b12)
OS:           Mac OS X 10.14.3 x86_64

gradleプロジェクトをkotlin-applicationを指定して作成します

$ gradle init --dsl kotlin

Select type of project to generate:
  1: basic
  2: cpp-application
  3: cpp-library
  4: groovy-application
  5: groovy-library
  6: java-application
  7: java-library
  8: kotlin-application
  9: kotlin-library
  10: scala-library
Enter selection (default: basic) [1..10] 8

Project name (default: api):
Source package (default: api):

BUILD SUCCESSFUL in 7s
2 actionable tasks: 2 executed

build.gradle.ktsにsparkの依存関係と必要な依存関係を追加します

build.gradle.kts
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Kotlin application project to get you started.
 */

plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
    id("org.jetbrains.kotlin.jvm").version("1.3.21")

    // Apply the application plugin to add support for building a CLI application.
    application
}

repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

dependencies {
    // Use the Kotlin JDK 8 standard library.
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // Use the Kotlin test library.
    testImplementation("org.jetbrains.kotlin:kotlin-test")

    // Use the Kotlin JUnit integration.
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    compile("com.sparkjava:spark-core:+")
    compile("org.slf4j:slf4j-log4j12:1.7.21")

}

application {
    // Define the main class for the application.
    mainClassName = "api.AppKt"
}

src/main/kotlin/api/App.kt を編集します

App.kt
package api

import spark.Spark.*

fun main(args: Array<String>) {
    get("/") { req, res -> "Hello World" }
}

gradleを実行します

$ gradle run

> Task :run
log4j:WARN No appenders could be found for logger (spark.route.Routes).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
<==========---> 80% EXECUTING [7s]
> :run

http://localhost:4567/
これでHello Worldが表示されます

DB操作

jdbcとjsonのマッパーの依存関係を追加します

build.gradle.kts
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Kotlin application project to get you started.
 */

plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
    id("org.jetbrains.kotlin.jvm").version("1.3.21")

    // Apply the application plugin to add support for building a CLI application.
    application
}

repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

dependencies {
    // Use the Kotlin JDK 8 standard library.
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // Use the Kotlin test library.
    testImplementation("org.jetbrains.kotlin:kotlin-test")

    // Use the Kotlin JUnit integration.
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    compile("com.sparkjava:spark-core:+")
    compile("org.slf4j:slf4j-log4j12:1.7.21")
    compile("org.jetbrains.kotlin:kotlin-jdbc:0.10.1316")
    compile("joda-time:joda-time:2.10.1")
    compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.8.8")
    compile("com.fasterxml.jackson.core:jackson-databind:2.8.8.1")
}

application {
    // Define the main class for the application.
    mainClassName = "api.AppKt"
}

App.ktを編集します

App.kt
/*
 * This Kotlin source file was generated by the Gradle 'init' task.
 */
package api

import spark.Spark.*
import spark.*
import java.sql.DriverManager
import java.util.*
import java.sql.Connection;
import java.sql.Statement
import java.sql.ResultSet
import org.joda.time.DateTime
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import spark.ResponseTransformer

data class Task(
        var id: Long,
        var name: String,
        // jdbcがDateTimeをうまく扱ってくれないのでString
        var createdAt: String
)

fun main(args: Array<String>) {
    Class.forName("com.mysql.jdbc.Driver");
    val toJson = JsonTransformer(ObjectMapper().registerKotlinModule())
    path("/tasks"){
        get("/:id", getTask(),toJson)
    }
}

fun getTask(): Route = Route { req, _ ->
        findTask(req.params("id").toInt())
}

fun findTask(id: Int): Task {
    lateinit var task: Task
    var statement: Statement? = null
    statement = getDBConnection()?.createStatement()
    var resultSet: ResultSet? = statement?.executeQuery("SELECT * FROM tasks WHERE id = ${id}")
    while(resultSet?.next() ?:false) {
        var sdf :java.text.SimpleDateFormat  = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
        var date = Date(resultSet!!.getTimestamp(3).getTime())
        task = Task(resultSet!!.getLong(1),resultSet!!.getString(2),sdf.format(date))
    }
    resultSet?.close()
    statement.close()
    return task
}

// DB接続
fun getDBConnection(): Connection {
    var conn: Connection = DriverManager.getConnection("jdbc:mysql://host:port/db","username","pass")
    return conn
}

// jsonにマッピング
class JsonTransformer(private val objectMapper: ObjectMapper) : ResponseTransformer {
    override fun render(model: Any?): String =
            objectMapper.writeValueAsString(model)
}

実行します

gradle run

http://localhost:4567/tasks/1
これでjsonが帰ってきます

ちなみにkotlinは入門難易度が高すぎです
このjson返すまで3時間かかりました笑笑笑

参考

[Python] Flask 入門
Python3からMySQL繋ぐ時は、いろいろあるけどとりあえずPyMySQLにしとこうや
Nim 言語で RESTful API 作ってみた
Nim言語感想&概説
至高の言語、Nimを始めるエンジニアへ
goでtime.Timeをmysqlから読む
Goでmysqlに接続する
Gradle 5.0 で作れるようになった build.gradle.kts と settings.gradle.kts
Kotlin+Spark FrameworkでAPI作ってみた
GradleでKotlinのプロジェクトを作成する

フレームワーク

Flask
Jester
Go Echo
Spark Framework

所属

レッドインパルス 株式会社ではインターン生を積極的に募集しています!
興味ある方はぜひ!!
株式会社POLでも募集しています!

148
145
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
148
145