Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@KeitaMoromizato

現場のSails.js #1 AltJS編

概要

弊社では一部の社内プロジェクトでSails.jsを利用しています。その中で得られたベストプラクティスやらバッドプラクティスをまとめていこうと思います。
第1回はバックエンド側のAltJSの活用から。

動作環境

  • node.js v0.12.7
  • Sails.js v0.11.2
  • [npm]sails-hook-babel v5.0.1
  • [npm]typescript v1.5.3
  • [npm]grunt-tsconfig-update v0.0.1

AltJSの活用

Sails.jsはnode.jsのフレームワークなので実際の開発ではAltJSを使用しています。TypeScriptで書いたコードを --target es6 に変換後、babel でES5に変換しています。

TypeScript

採用理由

個人的にTypeScriptが好きなのでバックエンド側コードは全面的にTypeScriptを採用しています。TypeScriptを導入するメリットは大きいと考えていて、モジュール間の整合性を厳密に定義出来る点が気に入っています。次回以降でアーキテクチャについては詳しく説明しますが、弊社ではViewController -> ApiController -> Service -> Modelという4層構造でバックエンドを構築しています。各層のAPIを変更したときに、デグレが発生していないかをコンパイル段階で気付けるのは良いですね。

設定方法

$ npm i typescript grunt-tsconfig-update grunt-shell --save-dev

Sails.jsにはGruntが組み込まれているのでこれを活用することにします。とは言えあまり依存したくもないので、tsconfig.jsonを出力するだけのモジュール grunt-tsconfig-update と、 grunt-shell で直接tscを叩く構成にしています。今見ると改善したくなってきた。。

tasks/config/tsconfig.js
module.exports = function(grunt) {
  grunt.config.set('tsconfig', {
    main: {}
  });

  grunt.loadNpmTasks('grunt-tsconfig-update');
};
tasks/config/shell.js
module.exports = function(grunt) {
  grunt.config.set('shell', {
    typescript: {
      command: "npm run ts"
    }
  });

  grunt.loadNpmTasks('grunt-shell');
};
tasks/register/buildServer.js
module.exports = function (grunt) {
  grunt.registerTask('build-server', [
    'tsconfig:main',
    'shell:typescript'
  ]);
};
package.json
  "scripts": {
    "ts": "./node_modules/typescript/bin/tsc -p ./"
  }

あとはtsconfigを用意して終わりです。

tsconfig.json
{
  "compileOnSave": false,
  "compilerOptions": {
    "target": "es6",
    "declaration": false,
    "sourceMap": false,
    "removeComments": true,
    "noLib": false,
    "preserveConstEnums": false,
    "moduleResolution": "node"
  },
  "filesGlob": [
    "./api/**/*.ts",
    "!./**/*.d.ts",
    "!./node_modules/"
  ],
  "files": [
  ]
}

コンパイル

$ grunt build-server

Babel

採用理由

最初に書いた通り、TypeScriptは--target es6で使っています。これはいつでもTypeScriptを捨てられるようにするためです。個人的にはずっとTypeScriptで書きたいのですが、書きたくない人に引き継ぐ可能性があるので。。

設定方法

sails-hook-babelというnpmを1つ入れるだけで終わりです。これでSails起動時に自動的にbabelで変換してくれます。ただ、このnpmは今のところbabel6対応をしていないため、長期メンテする予定のシステムでは使わない方が良いかもしれません。いつもどおり返還前のコードを置くディレクトリを用意して、普通にbabelを叩いてapi/*に出力するほうが良いのかも。

$ npm i sails-hook-babel --save

使用しているd.tsファイル

Sails用の型定義ファイルが無かったので自前で作って使っています。ただ公開出来るレベルでは書いていないので。。型定義ファイルにどれだけ作成コストをかけるのかというのは常に悩む。

sails.d.ts
/// <reference path="./typings/bundle.d.ts" />

declare var sails: Sails.sails;

declare module "sails" {
  import * as express from 'express';

  module s {
    interface Request extends express.Request {
      port: number;
      user: User;
      session: Session;
      sessionID: string;

      allParams(): Object;
      logout();
      file(name: String): any; // TODO file object
    }

    interface Response extends express.Response {
      ok(params: Object | number);
      view(view: string, params?: Object);
      json(params: Object);
    }

    interface Session {
      regenerate(callback: (error) => void);
    }

    interface User {
      id: number;
    }
  }

  export = s;
}

declare module Sails {

  interface sails {
    log: log;
  }

  interface log {
    verbose(...data: any[]);
    error(...data: any[]);
    warn(...data: any[]);
    debug(...data: any[]);
    info(...data: any[]);
    silly(...data: any[]);
  }
}

コードサンプル

実際のコードの抜粋です。雰囲気は伝わるかなと。

TagController.ts
/// <reference path="../../../sails.d.ts" />
/// <reference path="../../../typings/bundle.d.ts" />

import * as Sails from 'sails';

class TagController {
  'use strict';

  constructor() { }

  find(req: Sails.Request, res: Sails.Response) {
    sails.log.verbose('api::TagController#find');
    sails.log.verbose('params', req.allParams());
    sails.log.verbose(`user=${(req.user ? req.user.id : 'null') }`);

    const page = +req.param('page') || 1;

    TagService.find({
      limit: 30,
      page: page
    }).then((tags) => {

      res.ok(tags);

    }).catch((error) => {
      sails.log.error("api::TagController.find", error);
      res.send(500);
    });
  }
}

export default new TagController();

まとめ

TypeScript良いよね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
11
Help us understand the problem. What are the problem?