<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dala00</title>
    <description>The latest articles on DEV Community by dala00 (@dala00).</description>
    <link>https://dev.to/dala00</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F43797%2Fa2a94614-7da3-4429-a5a1-c554fcc74fd8.jpeg</url>
      <title>DEV Community: dala00</title>
      <link>https://dev.to/dala00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dala00"/>
    <language>en</language>
    <item>
      <title>Made service with Next.js and PlanetScale</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Sun, 08 May 2022 12:56:07 +0000</pubDate>
      <link>https://dev.to/dala00/made-service-with-nextjs-and-planetscale-28ng</link>
      <guid>https://dev.to/dala00/made-service-with-nextjs-and-planetscale-28ng</guid>
      <description>&lt;p&gt;I made a service 'Contact Nite' contact management service with contact form widget for Flutter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--knI-wIuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fseoi122b1sq0f0jsb46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--knI-wIuN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fseoi122b1sq0f0jsb46.png" alt="Eyecatch" width="768" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like above image, you can place contact form widget by just a easy code pasting.&lt;/p&gt;

&lt;p&gt;And you can see contacts in this service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YchqO-WH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5f2iktxjn7lz2udk0491.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YchqO-WH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5f2iktxjn7lz2udk0491.png" alt="Contact list" width="687" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Service&lt;br&gt;
&lt;a href="https://contact-nite.com/ja"&gt;https://contact-nite.com/ja&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pub&lt;br&gt;
&lt;a href="https://pub.dev/packages/contact_form"&gt;https://pub.dev/packages/contact_form&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free plan is provided. If you are interested in, try. I'm also using on my app.&lt;/p&gt;

&lt;p&gt;If contacts came, notify by email and Slack notification. And you can send and receive mail on the service. You don't need to open Gmail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology
&lt;/h2&gt;

&lt;p&gt;Package is a simple Flutter package. Service is made by Next.js, PlanetScale, Cloud Run.&lt;/p&gt;

&lt;h3&gt;
  
  
  PlanetScale
&lt;/h3&gt;

&lt;p&gt;PlanetScale is a Serverless MySQL Service. I knew it when I'm developing. &lt;/p&gt;

&lt;p&gt;Recently, I had been used Firestore when I make small web applications. Because there are no services cheap and large capacity. But when I found PlanetScale, I tried to change DB to it. Because it has free plan and large capacity. I like MySQL than NoSQL so I'm so excited.&lt;/p&gt;

&lt;p&gt;I think I always use PlanetScale when I make somethings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js
&lt;/h3&gt;

&lt;p&gt;Made by Next.js. API Server is also Next.js API Routes. So so so easy. There are nothing to think about client and server. Just make. If I work on job, it's not good to use API Routes. But on my indie hacking, I can't use others.&lt;/p&gt;

&lt;p&gt;Using Internationalized Routing for English and Japanese localization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;

&lt;p&gt;Using Cloud Run. Recently I'm using only this. So I'm now easy to develop with it.&lt;/p&gt;

&lt;p&gt;By default, Cloud Run deploys when code pushed. But testing is easier than GitHub Actions, doing like this: push -&amp;gt; Test on GitHub Actions -&amp;gt; Build on Cloud Build (Starts with Webhook from Actions) -&amp;gt; Deploy to Cloud Run.&lt;/p&gt;

&lt;p&gt;DB Migration is also executed on Cloud Build.&lt;/p&gt;

&lt;p&gt;On testing, using simple API test by Jest and E2E test with Cypress. Cypress is very interesting. On Cypress Dashboard, you can see movie of testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mail
&lt;/h3&gt;

&lt;p&gt;Using SendGrid. Using Dynamic Templates. You don't need manage mail templates on your service.&lt;/p&gt;

&lt;p&gt;And with receiving hook, saving received mail to DB. So you can send and receive mails only on web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flutter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Localization
&lt;/h3&gt;

&lt;p&gt;Localization was complicated. When develop package, use example project created with package. When developing on it, there were no problems. But when I use the package in other app, localization was not worked well. Fixing was difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freezed
&lt;/h3&gt;

&lt;p&gt;No problems. I could use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Package is public. I can use GitHub Actions with no limit. So I write some tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small story
&lt;/h2&gt;

&lt;p&gt;Contact form on 'Contact Nite' is using its contact form API.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>planetscale</category>
      <category>googlecloud</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Made a mobile app anyone can compose and play music by Flutter</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Tue, 02 Jun 2020 11:30:36 +0000</pubDate>
      <link>https://dev.to/dala00/made-a-mobile-app-anyone-can-compose-and-play-music-by-flutter-4l93</link>
      <guid>https://dev.to/dala00/made-a-mobile-app-anyone-can-compose-and-play-music-by-flutter-4l93</guid>
      <description>&lt;p&gt;I made a iOS and Android mobile app by Flutter. Anyone even doesn't have no knowledge about music can compose and play music by this app (only accompaniment).&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;音楽の知識がなくてもタップだけで演奏や作曲が出来るアプリを作りました（伴奏のみ）。合うコードだけ表示しているので適当なボタン操作でも曲が作れます。&lt;br&gt;外でふとコード進行が思い浮かんだ時にでも使ってみてください。 &lt;a href="https://twitter.com/hashtag/Flutter?src=hash&amp;amp;ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;#Flutter&lt;/a&gt;&lt;br&gt;Android&lt;a href="https://t.co/NESpvoRl5O" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/NESpvoRl5O" rel="noopener noreferrer"&gt;https://t.co/NESpvoRl5O&lt;/a&gt;&lt;br&gt;iOS&lt;a href="https://t.co/DgvsuStjwC" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/DgvsuStjwC" rel="noopener noreferrer"&gt;https://t.co/DgvsuStjwC&lt;/a&gt; &lt;a href="https://t.co/4lOTlAdXP9" rel="noopener noreferrer"&gt;pic.twitter.com/4lOTlAdXP9&lt;/a&gt;&lt;/p&gt;— だら🎄5/18～5/24開催Webサービス作るイベント (&lt;a class="mentioned-user" href="https://dev.to/dala00"&gt;@dala00&lt;/a&gt;) &lt;a href="https://twitter.com/dala00/status/1260561082080518145?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;May 13, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;I introduce technical things of this.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to use
&lt;/h2&gt;

&lt;p&gt;The basic usage is just tapping buttons. Music is automatically played with selected code and stroke way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb6m9dpmzvu01spxeqwv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb6m9dpmzvu01spxeqwv5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most important thing of this app is 'easy'. I wanted to make anyone who don't have no knowledge of music or children can use so easily. I thinks it's succeeded because my 7 years old son is playing music and singing original cute song by this app.&lt;/p&gt;

&lt;p&gt;Codes of buttons are minimum codes needed for one song. By this codes, you can play many songs in the world, and create. If you tap main, sub codes randomly, it becomes to a music automatically. You also can change pitched by up and down button at right bottom corner.&lt;/p&gt;
&lt;h3&gt;
  
  
  Save playing
&lt;/h3&gt;

&lt;p&gt;If you could play good feeling song, you can save it by 'Save last playing' button. And you can play it always.&lt;/p&gt;

&lt;p&gt;You don't need tap record button before playing. So you can save always and never lose opportunity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Change tempo and stroke
&lt;/h3&gt;

&lt;p&gt;You can change tempo by slider always.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh9ljdwe0lfgia4drgriv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh9ljdwe0lfgia4drgriv.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can select stroke way from some patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgfgzr4hekp55bvt2kpik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgfgzr4hekp55bvt2kpik.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to make users can create original pattern.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to make
&lt;/h2&gt;

&lt;p&gt;I describe how to make this app.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sound
&lt;/h3&gt;

&lt;p&gt;Flutter can't sound. So I wrote native code and connect to Flutter side. There's a document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/docs/development/platform-integration/platform-channels" rel="noopener noreferrer"&gt;Writing custom platform-specific code - Flutter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many Flutter plugins are using this way. So usually everybody can make app by only Dart code.&lt;/p&gt;
&lt;h4&gt;
  
  
  Ways to connect natives
&lt;/h4&gt;

&lt;p&gt;Specifically, write Dart code like this. Specify channel id to MethodChannel and send message to native side by invokeMethod.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'com.example.anyone-composer/midi'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="n"&gt;setTempo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'setTempo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'tempo'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is ways to receive message on Android and iOS. This is simple too. You can use many data types like written on document.&lt;/p&gt;

&lt;h4&gt;
  
  
  Android side
&lt;/h4&gt;

&lt;p&gt;Example on Android.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;configureFlutterEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@NonNull&lt;/span&gt; &lt;span class="n"&gt;flutterEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FlutterEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;GeneratedPluginRegistrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flutterEngine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;MethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flutterEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dartExecutor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binaryMessenger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.example.anyone-composer/midi"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setMethodCallHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"setTempo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"tempo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// nanikasuru&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  iOS side
&lt;/h4&gt;

&lt;p&gt;Example on iOS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;  &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;didFinishLaunchingWithOptions&lt;/span&gt; &lt;span class="nv"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LaunchOptionsKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;controller&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FlutterViewController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootViewController&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;FlutterViewController&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;midiChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"com.example.anyone-composer/midi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                              &lt;span class="nv"&gt;binaryMessenger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binaryMessenger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;midiChannel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMethodCallHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FlutterMethodCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="kt"&gt;FlutterResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"setTempo"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;tempo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"tempo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// nanikasuru&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kt"&gt;GeneratedPluginRegistrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;didFinishLaunchingWithOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Playing sound
&lt;/h4&gt;

&lt;p&gt;I used soundfont file (.sf2) and playing Midi. Actually, there is a Flutter package to play Midi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/flutter_midi" rel="noopener noreferrer"&gt;flutter_midi/FlutterMidiPlugin.java at master · rodydavis/flutter_midi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But some needed function is not present. For example change patch, specify velocity and stop all sounds. So I referenced the plugin and wrote codes on my own. It was so difficult because few document and samples are found...&lt;/p&gt;

&lt;h4&gt;
  
  
  Notes
&lt;/h4&gt;

&lt;p&gt;On Native side, you have to deal with threads. For example...&lt;/p&gt;

&lt;p&gt;Execute heavy processing&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/posts/Flutter-Android" rel="noopener noreferrer"&gt;Flutterのネイティブで重い処理をさせる方法（Android） - Crieit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Async processing with timer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/tamanyan/items/2a271886ba103c671a04" rel="noopener noreferrer"&gt;Swift マルチスレッドでの同期処理（synchronized）&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Draw playing animation
&lt;/h3&gt;

&lt;p&gt;When playing, app animate where is playing in stroke. It's just a simple animation that a red line goes to right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F136506%2F99212a15-820f-bd8c-163c-51a93ef9bd30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F136506%2F99212a15-820f-bd8c-163c-51a93ef9bd30.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's drawn by CustomPaint Widget of Flutter. You can draw anything by CustomPaint. CustomPaint uses a class overrides CustomPainter class.&lt;/p&gt;

&lt;p&gt;The way to use is so simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CustomPaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;painter:&lt;/span&gt; &lt;span class="n"&gt;StrokeAndLinePainter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strokeSets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;playerStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strokeIndex&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;playerStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is a example of actual painter class. Just using &lt;code&gt;canvas.drawFooBar&lt;/code&gt; methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;strokes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Paint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTWH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;horizontalMargin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;horizontalMargin&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;strokes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;barWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Animation
&lt;/h4&gt;

&lt;p&gt;CustomPaint is custom painting Widget. Not for animation. So you need to &lt;br&gt;
 create animation processing by yourself.&lt;/p&gt;

&lt;p&gt;I used timer and changing state. First of all, start timer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;      &lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;periodic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;_onTimer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm omitting some codes, this is a updating code. Getting current time from native side and set to Dart side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="nf"&gt;_onTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Timer&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;timeStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TimeStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;listen:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;timeStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This state is updated frequently. So I created dedicated store to redraw only animation part not to redraw all of page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;_buildStrokePaint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Consumer2&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;playerStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;timeStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CustomPaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;painter:&lt;/span&gt; &lt;span class="n"&gt;StrokeAndLinePainter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strokeSets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;playerStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strokeIndex&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="n"&gt;playerStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tempo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;timeStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'ts good for battery. I think there are other ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving playing data
&lt;/h3&gt;

&lt;p&gt;To saving data, I'm not saving actual sound data. Just saving the time when code changed and code. So very small data. Saving it to SQLite.&lt;/p&gt;

&lt;p&gt;When playing it, just changing code by saved timing. So you can play sounds with different tempo and strokes.&lt;/p&gt;

&lt;p&gt;This time, I wrote a following simple base model. And every models are override this base model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DbProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DbProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toMap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nl"&gt;where:&lt;/span&gt; &lt;span class="s"&gt;'id = ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;whereArgs:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DbProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;where:&lt;/span&gt; &lt;span class="s"&gt;'id = ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;whereArgs:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All models are override it and specify table name and write toMap method. Then you can deal data like following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Record&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'hoge'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'hoge2'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way is not good for large application, but I think it's good for small application like this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future dev
&lt;/h2&gt;

&lt;p&gt;I want to create such a futures. It's may not easy...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record void&lt;/li&gt;
&lt;li&gt;Create custom stroke patterns&lt;/li&gt;
&lt;li&gt;Edit saved playings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I thought
&lt;/h2&gt;

&lt;p&gt;I could use interesting things like native connection and CustomPaint.&lt;/p&gt;

&lt;p&gt;This time, I thought that I should develop all by Native without Flutter. But I got large merit to enable to make UI and dealing with data common by Flutter. I could reduce man-hours on my personally development.&lt;/p&gt;

&lt;p&gt;Thanks.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>swift</category>
      <category>kotlin</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Crieitの記事詳細ページが日本の技術系投稿サービスで最速になった</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Mon, 04 Mar 2019 00:11:56 +0000</pubDate>
      <link>https://dev.to/dala00/crieit-12fe</link>
      <guid>https://dev.to/dala00/crieit-12fe</guid>
      <description>&lt;p&gt;Crieitの記事詳細ページのレスポンスがdev.toのような速さになりました。実際にはdev.toよりも速いです。技術記事系サービスとしてはQiita等を含めて日本一となります。&lt;/p&gt;

&lt;p&gt;この画像は各サービスの記事ページのレスポンスにかかった時間を比較したものになります。タイミングによってばらつきもあるため速いものをメモしてみましたが、国内のQiitaやQrunchは170ミリ秒ほど、爆速で話題になったdev.toも意外にも30ミリ秒ほどで、平均だともうちょっと遅い感じがします。そしてCrieitは15ミリ秒くらいなのでダントツで最速です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dWXo3sr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rtwmqabkiikk1p1xe0rm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dWXo3sr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rtwmqabkiikk1p1xe0rm.png" alt="" width="600" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;確認方法としては、トップページから記事ページのリンクをクリックするか、記事ページでリロードするかが分かりやすいと思います。&lt;/p&gt;

&lt;p&gt;（当記事の以降の内容は対応直後でまだ検証中のため、誤った情報などがあればご指摘いただけると助かります）&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜ速いのか
&lt;/h2&gt;

&lt;p&gt;理由としては、Laravel側で動的にレンダリングしたHTMLをCloudflareというCDNでキャッシュしてもらっているため、サイトを閲覧している人のブラウザではそれが表示されているだけだからです。つまり、プログラム無しの単なるhtmlファイルを読み込んでいるのと同じレベルということです。実際に阿部 寛のホームページも大体17ミリ秒という感じで同じくらいの速さです。&lt;/p&gt;

&lt;p&gt;最近Gatsbyという静的サイトジェネレータが流行っています。これもコンテンツを事前にビルドし、サイト上では静的なファイルを表示するだけのため最速でコンテンツを届けることができるのですが、つまりはこれと全く同じレベルということです。しかもCloudflareのようなCDNは閲覧者の近くのサーバーから配信を行うため、ネットワークのレイテンシも非常に少なくあっという間にブラウザに表示されます。なので、速いのは何もすごくはなく、当たり前といえば当たり前のことです。&lt;/p&gt;

&lt;p&gt;mizchiさんもブログで仰っていました。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;フロントエンドの最適化の行き着く先の解の一つ、それは「CDN Edge で HTML をキャッシュして返却する」というものです。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://mizchi.hatenablog.com/entry/2019/02/21/235403"&gt;Edge Worker PaaS の fly.io が面白い - mizchi's blog&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  完璧なのか
&lt;/h2&gt;

&lt;p&gt;全然完璧ではありません。そもそも現在記事ページしか対応していないので、他のページは今までどおりです。&lt;/p&gt;

&lt;p&gt;さらには、各ページには一度誰かがアクセスしなければならないので、投稿後や編集後に限っては今までどおりですし、普段誰もアクセスしない記事などはキャッシュされていないので最初に誰かがアクセスした時に重さを味わってもらわなければなりません。具体的にはChromeのDev Tools等で見ると下記のようにHITというレスポンスになっていればキャッシュが効いている状態です。（MISSになっている場合はリロードするとその後はHITになります）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o27g6jAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/h87ygik3zp4kse4if6ud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o27g6jAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/h87ygik3zp4kse4if6ud.png" alt="" width="317" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;あと僕自身の理解も完璧とは言えないので、何かしら勘違いしている可能性もあります。一度でもbotがアクセスされればキャッシュされるという認識ですが、適当な記事にアクセスしてみると意外にキャッシュされていないことが多いです。まだリリースして間もない時期ということもあり不明点や十分な確認ができていない部分も多いため、このあたりは引き続き要確認です。&lt;/p&gt;

&lt;p&gt;ということで記事タイトルも文字数を気にしなければ「Crieitの記事詳細ページのHTML取得レスポンスはCDNのキャッシュが効いているページに限っては日本の技術系投稿サービスで最速になったが効いてなければすごく遅い」が正確です。&lt;/p&gt;

&lt;h2&gt;
  
  
  GatsbyやHugoとの違い
&lt;/h2&gt;

&lt;p&gt;最近流行っているGatsbyやHugoを使えばよいのでは、と思うかもしれませんが、これらは事前にビルドが必要なため、Crieitのようなユーザー投稿型のサービスで利用することはできません。（何か方法があるのかもしれませんが、どちらにしろリアルタイムで反映していくのは難しそうな気がします）&lt;/p&gt;

&lt;p&gt;そのためサーバーサイドでレンダリングしたものをそのままキャッシュしてもらうのが現実的な気がします。ビルドも不要です。&lt;/p&gt;

&lt;h2&gt;
  
  
  どんなメリットがあるのか
&lt;/h2&gt;

&lt;h3&gt;
  
  
  速い事自体が良い
&lt;/h3&gt;

&lt;p&gt;とにかく速い事自体がメリットです。遅いとやはりイライラしますし、検索エンジン的にも速ければ速いほど良いと思います。そしてアクセスした時の気持ちよさも大きいです。&lt;/p&gt;

&lt;h3&gt;
  
  
  サーバーの負荷もない
&lt;/h3&gt;

&lt;p&gt;CDN側で配信してくれるため、サーバーへのアクセスが来ません。そのためサーバーの負荷を抑えることができるようになります。実際にこのサーバーはGoogle Compute Engineの一番しょぼいサーバーで無料運用しているレベルのため、パフォーマンスも悪いしアメリカのリージョンのためレイテンシも大きいです。大体普通にアクセスすると１秒くらいかかってしまいます。（なので、dev.toは当然として、QiitaやQrunchも非常に高速という印象です）&lt;/p&gt;

&lt;p&gt;まあそれはそれほどの問題にはならないのですが、記事や投稿が増えてそれに伴ってページが増えていくと、サイトを勝手に巡回するよくわからないbotのアクセスもどんどん増えていってしまいます。普通に閲覧してくれるユーザーさんのアクセスだけだと全く問題にはならないのですが、そういうbotのアクセスまで含めてしまうと結構限界が来るのも早くなってしまうのではないかと思います。&lt;/p&gt;

&lt;p&gt;ですのでbotは全部もううちのサーバーに一切来なくなってほしいという思いからも対応を決意しました。&lt;/p&gt;

&lt;h2&gt;
  
  
  具体的にどうやったのか
&lt;/h2&gt;

&lt;h3&gt;
  
  
  そもそもCloudflareとは
&lt;/h3&gt;

&lt;p&gt;CDNというのは各サービスの静的コンテンツをキャッシュし、それを世界中に張り巡らされた配信ネットワークを利用して、最も近い拠点から配信してくれる、というものすごい仕組みです。&lt;/p&gt;

&lt;p&gt;例えばCloudflareではデフォルトではjs, css, 画像を配信してくれます。jsファイルやcssファイルは最近はビルドされていて数MBになっていることもあるため、サーバー上から配信するとかなり遅かったりします。うちでも初期はダウンロードに数秒かかっていました。CDNを通して配信すると一瞬で配信できるため、そもそもCloudflareがないと成り立たないレベルになっています。&lt;/p&gt;

&lt;p&gt;デフォルトでは上記のファイルしか配信されないのですが、Page rulesを設定すると他のパターンでも配信できるようになります。それを用いて記事のURLのパターンを設定し、配信されるようにしました。&lt;/p&gt;

&lt;h3&gt;
  
  
  ヘッダーの調整が必要
&lt;/h3&gt;

&lt;p&gt;しかし、ただPage rulesを設定してもキャッシュされません。というのもCloudflareはページのレスポンスヘッダの内容を見てキャッシュするかどうかを判断してくれます。キャッシュ期間を定めていればその期間でキャッシュを破棄して再度更新されるようにすることができますし、そもそも個人情報などを含んでいてキャッシュしてはならないページなどはキャッシュしないようになっています。&lt;/p&gt;

&lt;p&gt;例えばLaravelとかであればデフォルトで全てキャッシュを無効化するレスポンスヘッダが含まれていますので、一切キャッシュされません。そのため、Middlewareやルーティングを設定してキャッシュを許可するためのレスポンスヘッダを返す必要があります。&lt;/p&gt;

&lt;p&gt;具体的にはCache-Controlヘッダで下記を返すようにします。CDN用のMiddlewareグループを作成し、それ用のルーティングファイルにルーティングを記述するようにしています。ヘッダだけでなく、安全のためセッションも無効にしています。&lt;/p&gt;

&lt;h4&gt;
  
  
  public
&lt;/h4&gt;

&lt;p&gt;好き勝手にキャッシュしていいよ、というやつです。&lt;/p&gt;

&lt;h4&gt;
  
  
  s-maxage
&lt;/h4&gt;

&lt;p&gt;CDN向けのキャッシュ時間です。とにかく大きくして永久にキャッシュしてもらうようにしました。（実際にされているかどうかは検証できていませんが）&lt;/p&gt;

&lt;h4&gt;
  
  
  max-age
&lt;/h4&gt;

&lt;p&gt;ブラウザ向けのキャッシュ時間です。s-maxageを指定していない場合はCDNのキャッシュ時間にも用いられます。これは長くしすぎるとCDN側のキャッシュが切れたことに気づくことができませんので、短めにしています。ブラウザキャッシュが切れてもCDNのキャッシュを取ってくるだけなので、問題はありません。&lt;/p&gt;

&lt;h4&gt;
  
  
  Set-Cookieヘッダを送信しない
&lt;/h4&gt;

&lt;p&gt;Set-Cookieをレスポンスで返すと個人情報が含まれている可能性があると判断されるようでキャッシュが行われませんので送信しないようにします。&lt;/p&gt;

&lt;p&gt;Laravelであれば&lt;code&gt;\App\Http\Middleware\NoCookie::class&lt;/code&gt;のミドルウェアを追加します。ただ、webのルーティングでこのミドルウェアだけ追加しても動作しないようなので、別途CDN用のミドルウェアグループを作ると良いと思います。&lt;/p&gt;

&lt;h3&gt;
  
  
  キャッシュのpurgeが必要
&lt;/h3&gt;

&lt;p&gt;このままだと永久にキャッシュされてしまい、記事を更新しても反映されなくなってしまいます。そのため、キャッシュを破棄する必要があります。&lt;/p&gt;

&lt;p&gt;Cloudflareの管理画面で可能ですが、APIも用意されているためそれを用います。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/posts/PHP-Cloudflare-API"&gt;PHPでCloudflareのAPI経由でキャッシュを削除する&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;これで記事が更新された時にキャッシュをpurgeするようにしました。今のところは上手くいっている気がします。&lt;/p&gt;

&lt;h3&gt;
  
  
  ログイン状態をどう管理するか
&lt;/h3&gt;

&lt;p&gt;静的にキャッシュしてしまっているため、サーバーサイドのテンプレート上でログインしている場合だけこれを表示、みたいなことはできなくなります。そのため、VuexのStoreにログインしている状態を保持しておき、ページが表示されたあとに必要なところだけ置き換えるようにしました。&lt;/p&gt;

&lt;p&gt;置き換えると言っても単にVueコンポーネント化しておき、Storeの情報によって表示を切り替えているだけです。&lt;/p&gt;

&lt;h3&gt;
  
  
  リアルタイムのデータをどうするか
&lt;/h3&gt;

&lt;p&gt;リアルタイムのデータというのは、例えばキャッシュはできないけど表示が必要なコメントやアクセス数などです。これらはページが表示されたあと、ajaxで取って来ています。つまりキャッシュされていればまだ良いのですが、キャッシュされていない場合はページの描画を含めると２回通信していることになります。&lt;/p&gt;

&lt;p&gt;ただ、これは独自でアクセス数をカウントするため元々通信していたので変わらないのと、あとあくまでもやろうと思ったきっかけはbot対策のため、そのアクセスを軽減できれば問題ないかなと思っています。よくアクセスがあるページはだいたいキャッシュされた状態になると思いますし、そうでないページはさほどアクセスも来ないと思うので誤差かなと思います。とにかくbotを無視して、実際に見に来てくれる方の負荷だけを気にすれば良くなると考えると大きなメリットだと思っています。&lt;/p&gt;

&lt;h2&gt;
  
  
  問題点等の考察
&lt;/h2&gt;

&lt;p&gt;まだ謎や問題点があるので考察していきます。単なる知識不足の勘違いの可能性もあるのでわかる方は是非アドバイスをお願いします。また、この考察はあくまでもCloudflareの話ですので、他のCDNサービスの場合は全然関係ない可能性もあります。&lt;/p&gt;

&lt;h3&gt;
  
  
  キャッシュされていない？
&lt;/h3&gt;

&lt;p&gt;Google Analyticsでアクセスされたことが確認できたページや、サーバーのログでbotが来たらしきページにアクセスしてみるのですが、キャッシュが効いていない事が多いです。（前述のヘッダがHITではなくMISSになる）&lt;/p&gt;

&lt;p&gt;そのため上手く期限が設定できていなかったり、そもそもCDN自体の仕様を勘違いしているのではないか、と心配になりました。ただ、もしかするとネットワークのエリア毎にキャッシュをしているようであれば、そのようになる可能性もあるのかな、と思いました。&lt;/p&gt;

&lt;p&gt;前述のように近いエリアからコンテンツが配信されるので、必ずしも全てのCDNサーバーがキャッシュを持っているとは限りません。確かにそのあたり細かく連携するとレスポンスの速さが失われる要因になるような気もするので、エリアごとの管理で十分なのかなという気はします。purgeは多分全て連動で行ってくれるのかな、と思います。（どこかで瞬時に削除する、というような記述を見たような）&lt;/p&gt;

&lt;p&gt;もしくはbotっぽいアクセスはキャッシュを使わず素通りさせるようにしているとか？&lt;/p&gt;

&lt;h3&gt;
  
  
  アップデート時にpurgeが必要
&lt;/h3&gt;

&lt;p&gt;あとで気づいたのですが、よくよく考えると何かしら機能を更新してリリースしても、HTMLが変わらないのでリリースしたことが反映されません。ビルド済みのJavaScriptファイルはバージョニングされているのですが、それも新しいバージョンのものを見てくれないので、結局何かアップデートした際にはpurgeが必要で、また全てのページが一度重い状態になります。頻繁にアップデートすればするほどキャッシュが効かない状態が多くなります。（とはいえ対応前と変わらない状況ではありますが）&lt;/p&gt;

&lt;p&gt;そのためとりあえず手動全purgeするか、しんどくなってきたら今手動でやっているデプロイを自動デプロイできるようにしてその中に組み込むか、あとはCDN芸をやめて最速の座から降りるか、というところになりそうです。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;とりあえずCloudflareを使った無料でのCDN芸をやってみたかった、というところもあったので試してみました。有料にはなりますが、他のCDNサービスであればもうちょっと楽な運用ができるのではないかと思います。もしログイン情報をあまり使っていないサービス等であればすぐにでも導入できたりするかもしれません。&lt;/p&gt;

&lt;p&gt;Crieit上でもボードのデータ数が増えてきているため、こちらもCDN化を試しているところです。プライベート機能があるのでそのあたりでちょっとコツが必要だったりします。また、サーバー側のアクセスログもどういう風にアクセス数が推移しているかも解析してみたいなと思っています。また面白い情報が出たら記事にしてみます。（詳しく設定を確認していないのでもう消えているかもしれませんが…）&lt;/p&gt;

</description>
      <category>cdn</category>
      <category>laravel</category>
      <category>vue</category>
      <category>japanese</category>
    </item>
    <item>
      <title>NodeでChromeを操作してTwitterシェア用画像を生成するサーバー作った</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Tue, 12 Feb 2019 06:59:41 +0000</pubDate>
      <link>https://dev.to/dala00/nodechrometwitter-a04</link>
      <guid>https://dev.to/dala00/nodechrometwitter-a04</guid>
      <description>&lt;p&gt;Node.jsにはPuppeteerという画面を表示せずにChromeを扱えるライブラリがあります。それを利用し、URLをパラメータとして与えるだけでスクリーンショットを画像としてブラウザで表示させることができるサーバーアプリケーションを作成しました。&lt;/p&gt;

&lt;p&gt;実際にこのような画像が作成できます。例として下記はボード内投稿にコードを入れた場合に表示される画像です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4Dp5uys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rcq2nitt8602wdt78ozb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4Dp5uys--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rcq2nitt8602wdt78ozb.png" alt="コードを表示したOGP例" width="600" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下記は記事の画像です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Iujn9Ytd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/84ncripwkyjk29mcj2pd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Iujn9Ytd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/84ncripwkyjk29mcj2pd.png" alt="記事のOGP例" width="600" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HTML, CSSから生成しているためドロップシャドウとかもこんな感じで簡単に効かせる事ができています。&lt;/p&gt;

&lt;p&gt;GitHubで公開していますので、とりあえず使って見たいという方は記事を読み飛ばして最後の方にあるリンクをご利用ください(こらえきれぬ涙で頬を濡らしながら)&lt;/p&gt;

&lt;h2&gt;
  
  
  何が便利なのか
&lt;/h2&gt;

&lt;p&gt;WebサービスのURLをTwitter等でシェアするだけで、URLだけでなくツイートに画像も表示されるのをよく見ると思います。&lt;/p&gt;

&lt;p&gt;これは画像をアップしているわけではなく、そのページ内のHTMLに書かれているメタタグで指定されている画像を勝手にSNS側で表示してくれるという便利な、いわゆるOGPというものです。&lt;/p&gt;

&lt;h3&gt;
  
  
  Webサービスでの困りごと
&lt;/h3&gt;

&lt;p&gt;普通のサイトであれば画像を指定するだけですが、Webサービスの場合は投稿された内容をもとにしたOGPを動的に作りたいという場合があります。&lt;/p&gt;

&lt;p&gt;この場合画像を動的に作成しなければならないのですが、これがなかなか面倒です。&lt;/p&gt;

&lt;p&gt;サーバーサイドでやると、負荷がかかったりそもそも描画がプログラムによる命令で構築されることが多く、メンテナンスが非常に面倒です。フロントでやることもできますが、ブラウザの閲覧環境によって表示に違いが出てしまったりします。&lt;/p&gt;

&lt;h3&gt;
  
  
  解決策
&lt;/h3&gt;

&lt;p&gt;そのため、専用のPuppeteerを用いたOGP生成サーバーを用意することで前述の全ての問題を解決することができます。&lt;/p&gt;

&lt;p&gt;OGPはURLを指定するだけのため、サーバーを別にすることもできますので、分けておけばアプリケーションサーバー側に負荷は一切ありません。また、OGPサーバーのブラウザで表示を行いスクリーンショットをとるため、ユーザーの環境にも依存しません。&lt;/p&gt;

&lt;h2&gt;
  
  
  具体的な仕組み
&lt;/h2&gt;

&lt;p&gt;今回作成したものは、URLを指定するとそのURLのスクリーンショットを撮って画像として表示してくれる、というだけの非常にシンプルなものです。&lt;/p&gt;

&lt;p&gt;例えば最初の記事の画像であれば、実際の &lt;a href="https://crieit.net/posts/Crieit-5b91bd1569dbd"&gt;なぜCrieitを作ろうと思ったか&lt;/a&gt; のページにアクセスし、ソースを見るとわかりますが、下記のようなメタタグがあります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://ogp.crieit.net/posts/Crieit-5b91bd1569dbd/ogp.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:card"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"summary_large_image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://ogp.crieit.net/posts/Crieit-5b91bd1569dbd/ogp.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;この&lt;code&gt;og:image&lt;/code&gt;と&lt;code&gt;twitter:image&lt;/code&gt;に指定されている画像が今回紹介する仕組みで作成されている画像です。&lt;/p&gt;

&lt;p&gt;具体的には、crieit.netの全く同じパスのスクリーンショットを撮る仕組みになっています。つまり、上記であれば下記のURLのスクリーンショットを撮っています。実際に見ていただくことも可能です。（拡張子はつけれるようにしています。理由は後述します）&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/posts/Crieit-5b91bd1569dbd/ogp"&gt;https://crieit.net/posts/Crieit-5b91bd1569dbd/ogp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;アクセスしていただくとわかりますが、OGP画像をそのまま単なるHTMLとCSSで作っているだけになります。単にそのページのスクリーンショットを撮っているだけです。この例ですとフォントが違うと思いますが、それはOGPサーバー内にインストールしているのでOGPサーバー内のChromeでは正しいフォントで表示されます。&lt;/p&gt;

&lt;p&gt;ちなみにブラウザでのレンダリングのため、JavaScriptも動きます。今回のコード表示の例もhighlight.jsを動かしています。&lt;/p&gt;

&lt;h3&gt;
  
  
  URLに拡張子
&lt;/h3&gt;

&lt;p&gt;前述の例のように、OGPサーバー側のURLには拡張子をつけることができます。これにより、CloudflareのようなCDNを介する場合、ちゃんとキャッシュしてくれるようになります。&lt;/p&gt;

&lt;p&gt;つまり、そのURLに一度誰かがアクセスしていればその後はCDNで配信されるようになるため、OGPサーバー側にはアクセスが来なくなります。&lt;/p&gt;

&lt;p&gt;実際に前述の画像を開いてリロードするとめちゃくちゃ速いのがわかると思います。たいした処理ではないにしろ、本来はブラウザでページを開くだけの時間がかかりますので全然変わってきます。&lt;/p&gt;

&lt;p&gt;とはいえそもそもOGPはだいたいSNS側でキャッシュされるためあまりメリットは大きくないのですが、改造すれば他の用途にも使用することができますので、この仕組みを覚えておけば色々と便利ではあると思います。&lt;/p&gt;

&lt;h3&gt;
  
  
  構成要素
&lt;/h3&gt;

&lt;p&gt;Puppeteerを使うのでNode.jsのサーバーとなります。&lt;/p&gt;

&lt;p&gt;サーバーはExpressで、expressコマンドで作成したソースのほとんどそのままです。複雑なことはしていないので数十行書いたくらいで完成しました。(そのため不要な処理もたくさん残っていて作り自体は雑です…)&lt;/p&gt;

&lt;h3&gt;
  
  
  functions系の利用は断念
&lt;/h3&gt;

&lt;p&gt;現在色々なPaasやfunctionsサービスがありますが、色々調べたり試した結果利用は断念し、最終的にシンプルにExpressによるNode.jsのサーバーで作ることにしました。&lt;/p&gt;

&lt;p&gt;断念した理由は下記等です。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;リクエスト毎にブラウザを起動しなければならないため、画像生成にかなり時間がかかりそう。Expressであれば最初に一度起動しておくだけでよい&lt;/li&gt;
&lt;li&gt;サービスによってはそもそも使えない&lt;/li&gt;
&lt;li&gt;サービス上では特殊な実装にしなければならない場合があるので、ローカルで試しづらく開発に時間がかかって面倒な場合がある&lt;/li&gt;
&lt;li&gt;サービスによってはプロセスの実行時間制限があったりする&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…等です。もしうまく簡単に動かせるサービスがあったらぜひ教えてください。&lt;/p&gt;

&lt;h2&gt;
  
  
  サーバー
&lt;/h2&gt;

&lt;p&gt;実際のサーバーの作り方を解説していきます。&lt;/p&gt;

&lt;h3&gt;
  
  
  Chromeをインストール
&lt;/h3&gt;

&lt;p&gt;一番重要な仕事をしてくれるものです。&lt;/p&gt;

&lt;p&gt;ChromeDriverを試していた時の名残で一部不要なものが混じってるかもしれません。これはDockerfileの中身そのままです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get update
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libappindicator1 fonts-liberation unzip
curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; google-chrome&lt;span class="k"&gt;*&lt;/span&gt;.deb &lt;span class="o"&gt;||&lt;/span&gt; apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  フォントをインストール
&lt;/h3&gt;

&lt;p&gt;Webフォントだけでページを作れば不要ですが、そうでない場合や不足のフォントがありそうな場合にはインストールしておきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;fonts-ipafont-gothic fonts-ipafont-mincho
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.jsをインストール
&lt;/h3&gt;

&lt;p&gt;とりあえずnpmとyarnを入れておきます。公式サイトに書かれている手順で入れておけば問題ないと思います。&lt;/p&gt;

&lt;h3&gt;
  
  
  npmのモジュールーインストール
&lt;/h3&gt;

&lt;p&gt;Chromeはすでにインストールしているため、それをスキップする形でインストールします。違うパターンでインストールしたい場合は適宜変更してください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PUPPETEER_SKIP_CHROMIUM_DOWNLOAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nginxをインストール
&lt;/h3&gt;

&lt;p&gt;これは必須ではないのですが、ポート3000で動いているNode.jsサーバーを80にプロキシしたかったのと、SSLに対応したかったため同じくLet's Encryptのcertbotが使用するURLもプロキシして放置で証明書が更新されるようにしたかったためNginxで動かすようにしています。（ここもわりと適当なため適宜変更してください）&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        listen 443 ssl default_server;
        ssl_certificate /etc/letsencrypt/live/ogp.example.net/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/ogp.example.net/privkey.pem;

        server_name ogp.example.net;

        proxy_redirect off;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;

        location ^~ /.well-known/ {
                root /var/www/html;
        }

        location / {
                proxy_pass http://localhost:3000/;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Systemdでサービス化
&lt;/h3&gt;

&lt;p&gt;下記のように設定しました。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ogp server&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;syslog.target network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/npm start&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/path/to/puppeteer-ogp&lt;/span&gt;
&lt;span class="py"&gt;KillMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mixed&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourname&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourgroup&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;だと、理由はまだよく分かりませんがうまくアプリケーションやChromeのプロセスが終了してくれないため、&lt;code&gt;KillMode=mixed&lt;/code&gt;にして丸々強制終了するようにしています。&lt;/p&gt;

&lt;p&gt;サービスの有効化は下記です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;ogp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;あとは下記で起動すれば完全放置できます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service ogp start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ちなみに僕は無料枠で動かせるGoogle Compute Engineのf1-microを使っています。&lt;/p&gt;

&lt;h2&gt;
  
  
  公開しました
&lt;/h2&gt;

&lt;p&gt;かなり何もやってないレベルの製作物だったので、作成したものをGitHubで公開しています。是非適当に試してみたり、改造して遊んでみたりしてみてください。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dala00/puppeteer-ogp"&gt;dala00/puppeteer-ogp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下記に重要な注意点を書きます。そのあとローカルでの開発方法もざっと書いておきます&lt;/p&gt;

&lt;h2&gt;
  
  
  注意点
&lt;/h2&gt;

&lt;p&gt;まだ動かしてからさほど経っていないため、どれくらい安定稼働してくれるかは謎です。というのも、アプリケーションとは別にChromeが動いているので、そのあたりで安定性がどうなのかという部分が全く想像できません。一応最後に調整をしてからは問題ないようなのですが、稼働初期はクラッシュしまくっていました。&lt;/p&gt;

&lt;p&gt;ですので利用される場合は自己責任でよろしくお願いします。一応どういう対策を入れたかと、どういう問題がありそうかをメモしておきます。&lt;/p&gt;

&lt;h3&gt;
  
  
  エラー処理を入れる
&lt;/h3&gt;

&lt;p&gt;ブラウザがクラッシュする前提で、ページを開くときとかにエラーをcatchするようにし、問題があれば再起動するようにします。ただ、確認が不十分のため現在のエラー処理自体が正しくない可能性があります。実際にクラッシュするとプロセスがどんどん増えていく問題を確認済みです。&lt;/p&gt;

&lt;p&gt;おそらくですが、デーモン化して確実に再起動される状態であれば、問題が出たらそのままアプリケーションを落としてしまう方が安心では、という気がしています。（そうなると安定性と高頻度アクセスを保証すべきようなアプリケーションでは難しいかもしれませんが）&lt;/p&gt;

&lt;h3&gt;
  
  
  URLのフィルタを入れる
&lt;/h3&gt;

&lt;p&gt;これが今のところ一番の改善になりました。というのも、サーバーを公開していると.gitフォルダやphpMyAdminを無差別に探してデータを盗もうとする不審なアクセスがあります。これが一瞬のあいだに高頻度で行われるのでChromeへの負荷が高まりクラッシュしていました。&lt;/p&gt;

&lt;p&gt;そのため下記のような感じで環境変数にて必要なURLだけをフィルタできるようにし、それ以外のURLの場合はChromeを使わないようにしました。これだけでクラッシュが発生しなくなりました。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URL_FILTER=/articles,/posts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;とにかく、いかにChromeを使用しないか、というところが重要になってくる気がします。アクセスが多い場合、待機させるようにして同時にたくさんのページを開かないような形にすると安定性が上る可能性もありそうです。（ただしレスポンスが悪くなるとSNSがOGPを認識してくれない可能性も出てきそうですが）&lt;/p&gt;

&lt;h3&gt;
  
  
  多分結構スペックが高いサーバーの方が良さそう
&lt;/h3&gt;

&lt;p&gt;Chrome自体が低スペック環境でガンガン快適に動くようなものではないので、サーバーもスペックが高いほうが良さそうな気がします。実際に現在使っているGoogle Compute Engineのf1-microインスタンスでは、マシンタイプをグレードアップしたほうが良い、というアラートがずっと出っぱなしになっています。&lt;/p&gt;

&lt;p&gt;また、おそらくスペックが高いほうがクラッシュする可能性も低くなるのでは…と想像しています。&lt;/p&gt;

&lt;h3&gt;
  
  
  参考にした情報
&lt;/h3&gt;

&lt;p&gt;たしか下記あたりを参考にしました。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://uyamazak.hatenablog.com/entry/2019/01/30/150910"&gt;Headless Chromeを使ったPDF変換サーバーが落ちないようにした対策まとめ - GAミント至上主義&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qiita.com/go_sagawa/items/4a368040fac6f7264e2c"&gt;puppeteerを永続化したNode.jsアプリ内で安定稼働させる方法 - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qiita.com/windyakin/items/00b085902547570eebc6"&gt;Docker上のpuppeteerがPage crashしてしまうときはshmサイズを疑う - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ローカルでの開発方法
&lt;/h2&gt;

&lt;p&gt;基本的にはGitHubにあげているREADMEそのままですが、Dockerとdocker-composeさえ入っていれば誰でもすぐ試せるようになっています。Getting StartedとDevelopmentのところをそのまま行うだけで動きます。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;ちょっと改造すればひとつのサーバーで複数のサービスのURLに対応することも可能ですし、特に個人開発している人はひとり一OGPサーバーを持っておくとなにかと便利な気がします。&lt;/p&gt;

&lt;p&gt;コードのOGPはよろしければ下記で色々投稿して試してみてください！&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/boards/try-code-ogp"&gt;コードのOGPを試してみるボード&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;もし問題やプルリクエストがある場合はお気軽にGitHubに立ててください。（余裕のあるときしか見れないかもしれませんが…）&lt;/p&gt;

</description>
      <category>node</category>
      <category>puppeteer</category>
      <category>express</category>
      <category>japanese</category>
    </item>
    <item>
      <title>BitBucketからGitHubのプライベートリポジトリに引っ越した</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Tue, 08 Jan 2019 00:53:58 +0000</pubDate>
      <link>https://dev.to/dala00/bitbucketgithub-2n4a</link>
      <guid>https://dev.to/dala00/bitbucketgithub-2n4a</guid>
      <description>&lt;p&gt;GitHubのプライベートリポジトリが無料でも作れるようになりました！　無料で共有できるのは３人までですが、僕がやっているような個人開発は一人で使うことが多いため非常に嬉しいです。&lt;/p&gt;

&lt;p&gt;ずっとプライベートリポジトリとしてBitBucketを使っていましたが、重かったり使いづらかったりで不満はあり、GitHubを使いたかった気持ちはあったので、とりあえず一番頻繁に使っているCrieitのリポジトリをBitBucketからGitHubに引っ越ししてみました。&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHubでプライベートリポジトリを作成
&lt;/h2&gt;

&lt;p&gt;まずはGitHubにて空のプライベートリポジトリを作成しておきます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzub3lwl47trnrwylhj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzub3lwl47trnrwylhj2.png" alt="Create a New Repository.png" width="754" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  各環境のremote参照を変更
&lt;/h2&gt;

&lt;p&gt;開発環境、本番環境など、Gitで参照しているremoteのURLを変更します。とりあえず現状のURLを見てみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
origin  git@bitbucket.org:alphabrend/dev.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
origin  git@bitbucket.org:alphabrend/dev.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;URLを変更します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote set-url origin git@github.com:dala00/crieit.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;再度確認してみると変更されていることがわかります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
origin  git@github.com:dala00/crieit.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
origin  git@github.com:dala00/crieit.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;実際にpushしたりする際にはGitHubの方にも鍵を登録しておく必要があります。ローカルはBitBucketにもGitHubにも登録しているかもしれませんが、本番環境は個別に登録していく必要がある場合が多そうです。&lt;/p&gt;

&lt;p&gt;諸々準備できたらfetchしたりpushしたりを試してみてください。（まだざっとしか試していないのでうまくいかなかったらすみません…）&lt;/p&gt;

&lt;h2&gt;
  
  
  草も移動するっぽい
&lt;/h2&gt;

&lt;p&gt;多分ちゃんと草も移動してくれてるっぽい（？）です。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp35mavqasepa09a0aiy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp35mavqasepa09a0aiy.png" alt="contribution.png" width="627" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;あとはCIとかも引っ越す必要がありますのでまた対応したら記事を書いていきます。&lt;/p&gt;

&lt;p&gt;Crieitはdev.toとQiitaを参考に作った、というところからdevという変なリポジトリ名になっていたのですが、この機会にちゃんとしたリポジトリ名にできたのも良かったです。&lt;/p&gt;

</description>
      <category>bitbucket</category>
      <category>github</category>
      <category>japanese</category>
    </item>
    <item>
      <title>Laravel Mixにて途中でJavaScriptをTypeScriptに変える</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Sun, 30 Dec 2018 02:06:30 +0000</pubDate>
      <link>https://dev.to/dala00/laravel-mixjavascripttypescript-1fbe</link>
      <guid>https://dev.to/dala00/laravel-mixjavascripttypescript-1fbe</guid>
      <description>&lt;p&gt;Laravel MixでVueを使っているプロジェクトで、既にコンポーネントをJavaScriptで書いてしまったけどTypeScriptに変えたい、という場合があると思いますが、割と最小限ではじめられます。全部のコンポーネントをTypeScriptに書き直さなければならない、ということもありませんし、おすすめです。具体的な手順を書いておきます。&lt;/p&gt;

&lt;h2&gt;
  
  
  必要なファイルを追加
&lt;/h2&gt;

&lt;p&gt;vue-cliやcreate-nuxt-appのテンプレートから取ってくると早いと思います。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tsconfig.json&lt;/li&gt;
&lt;li&gt;resources/assets/js/index.d.ts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  mix.jsをmix.tsに変える
&lt;/h2&gt;

&lt;p&gt;webpack.mix.jsで下記のような設定があると思います。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resources/assets/js/app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これをtsに変えます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resources/assets/js/app.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;まるごと全てTypeScriptに置き換える開設の場合はフォルダ名もtsに変更する様な記述があったりしますが、今回は最小限ですのでメソッド名とapp.jsのファイル名だけの変更になります。&lt;/p&gt;

&lt;h2&gt;
  
  
  app.jsをapp.tsにする
&lt;/h2&gt;

&lt;p&gt;app.jsというかエントリーポイントのファイルですが、これだけはTypeScriptに変更しないといけません。そのため変更し、ビルド時にエラーが出るようであればとりあえず変数の宣言に&lt;code&gt;variable: any&lt;/code&gt;のようにanyを付けまくります。とりあえずビルドできないとしょうがないので型の設定はあとでやることにしてひたすらanyを付けていきます。&lt;/p&gt;

&lt;h2&gt;
  
  
  コンポーネントをTypeScript化する
&lt;/h2&gt;

&lt;p&gt;とりあえず何か一つTypeScriptに変更してみましょう。これは他で色々解説があるので詳しくはそのあたりを見ていただければ良いと思いますが、一応ざっと何をするかを書いておきます。&lt;/p&gt;

&lt;h3&gt;
  
  
  クラスにする
&lt;/h3&gt;

&lt;p&gt;これがだいたい基本形です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-property-decorator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;YourComponentName&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  いくつかの設定はデコレータ側へ
&lt;/h3&gt;

&lt;p&gt;使うコンポーネントの設定などはデコレータ側で設定します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SubComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  プロパティもデコレータ
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dataは使わず全部クラスのプロパティにする
&lt;/h3&gt;

&lt;p&gt;before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aaa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="na"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nx"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aaa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;YourType&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  メソッドは普通のメソッドに
&lt;/h3&gt;

&lt;p&gt;methods配下には書かず直接クラスのメソッドにする&lt;/p&gt;

&lt;h3&gt;
  
  
  computedはgetterに
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;computedId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`ID:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  その他
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ts-loaderがエラーになったらバージョンをあわせてインストール&lt;/li&gt;
&lt;li&gt;型が入っていない外部モジュール等は@typesに型があればインストール&lt;/li&gt;
&lt;li&gt;型がない外部モジュールはindex.d.tsにdeclareを追加（VSCodeなどであれば対処方法が表示されていると思います）&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>vue</category>
      <category>typescript</category>
      <category>japanese</category>
    </item>
    <item>
      <title>Nuxt.js+Firestoreの場合に安全にSSRする方法</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Sun, 16 Dec 2018 14:35:55 +0000</pubDate>
      <link>https://dev.to/dala00/nuxtjsfirestoressr-231g</link>
      <guid>https://dev.to/dala00/nuxtjsfirestoressr-231g</guid>
      <description>&lt;p&gt;Nuxt.jsをサーバーで動かしてFirestoreを使い、且つサーバーサイドレンダリングしたい時には色々考えることが出てきます。懸念点と対応した方がよさそうな点をまとめてみます。&lt;/p&gt;

&lt;h2&gt;
  
  
  SSRしない場合との違い
&lt;/h2&gt;

&lt;p&gt;SSRせずクライアント上でFirestoreからデータを取る場合ですが、匿名認証にしろ、SNSアカウントの認証にしろ、認証しているかどうかでセキュリティルールによりデータを守りやすく、他者によるFirebaseプロジェクトの設定を盗用しての勝手な操作を防いだりすることができます。&lt;/p&gt;

&lt;p&gt;しかし、asyncData内によるSSRとなると、そのクライアント側の認証情報を利用することはできません。そうなるとデータを守れないどころか、そもそもセキュリティルールが設定されているとデータにアクセスすることすらできません。こういう時にどうすれば良いかを考えます。&lt;/p&gt;

&lt;h2&gt;
  
  
  どのように解決するか
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cloud Functionsの利用
&lt;/h3&gt;

&lt;p&gt;今回はCloud Functionsを利用する方法を考えました。具体的にどうするかというと、asyncData内ではfunctionsからデータを取るだけにします。例えば下記のような感じです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;asyncData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;functionUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;呼び出し方はどうであれ、通信が発生するので上記のようにリクエストは１回にした方が良いでしょう。functions側でそのページ専用のアクションを作ってデータを返します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onRequest&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUserByUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  データの取得方法
&lt;/h3&gt;

&lt;p&gt;しかし、これでは先程のasyncDataと同様、セキュリティルールの関係でデータが取れないのでは？　と思われるかもしれません。ただ、Firebaseにはサーバー用のFirebase Admin SDKというものがあります。これはセキュリティルールに関係なくデータを扱うことができるため、これを利用します。具体的には下記のような形です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase-admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;querySnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;usersCollection&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;querySnapshot&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;クライアント側とは微妙にcollectionの取り方が違うだけで、基本的にはほぼ同じです。このようにしてサーバーサイドレンダリングのためのデータ取得処理を行うことができます。&lt;/p&gt;

&lt;h2&gt;
  
  
  セキュリティルールを無視で良いのか？
&lt;/h2&gt;

&lt;p&gt;今回の例のfunctions側でのデータ取得処理に関しては、ルールを無視で問題ありません。&lt;/p&gt;

&lt;p&gt;というのも、そもそもSSRの目的としては、クローラに認識してもらうためのSEO対策になるかと思います。つまり、ユーザー認証が必要なデータを取る必要は一切無いということになります。あくまでも誰もが閲覧できるパブリックなデータを取るだけです。そのため基本的には認証に関するセキュリティルールをこの場で考慮する必要はありません。ただ、corsはちゃんと挟んでおきましょう。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;やってみて思いましたが、これって普通のサーバーを使った開発と同じですよね！？　なんとなく手軽さが失われた感じはありますが、それでもまあサーバーを管理する必要が無いのは大きなメリットではあることに変わりはないでしょう。&lt;/p&gt;

&lt;p&gt;他にも色々と方法はあるかもしれませんが、とにかくどの様な方法にしろ、どのようにデータを守るかはしっかり考えて構築が必要となります。&lt;/p&gt;

&lt;p&gt;以前認証とセキュリティルールについて書いた考察もありますので、もし気が向いたらそちらもぜひ御覧ください。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/posts/Firebase"&gt;Firebaseの匿名認証はなんのためにあるのか - セキュリティ編&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>firebase</category>
      <category>firestore</category>
      <category>japanese</category>
    </item>
    <item>
      <title>RDBとFirebaseのDB両方使ったっていいじゃない</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Tue, 27 Nov 2018 13:14:57 +0000</pubDate>
      <link>https://dev.to/dala00/rdbfirebasedb-4n4b</link>
      <guid>https://dev.to/dala00/rdbfirebasedb-4n4b</guid>
      <description>&lt;p&gt;今年Firebaseを使い始めてからちょこちょこ自分の中で議題に上がるのが、RDBとFirebaseのデータベース（Firestore、Realtime Database）のどちらが使い勝手良いのか、ということ。ただ、そもそもどちらかを選ばなきゃいけないというわけではなく、適宜両方使えばいいということが分かったのでそれについての雑記。&lt;/p&gt;

&lt;p&gt;そもそもまず、なぜ悩むところがあったのか。&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebaseのデータベースのデメリット
&lt;/h2&gt;

&lt;p&gt;一番大きいのはやはりリレーショナルでないことと、クエリが弱いということ。集計ができなかったり、欲しいデータがすぐ取れなかったりする。&lt;/p&gt;

&lt;h2&gt;
  
  
  RDBのデメリット
&lt;/h2&gt;

&lt;p&gt;とりあえずRDBにしておけば何でもできるというのはあるが、デメリットとしてはやはりサーバーもしくはお金が必要になってくるということ。サーバー内に入れると管理やスペックの調整などが気になるし、別途RDSやCloud SQLを使うと当然お金がかかる。&lt;/p&gt;

&lt;p&gt;Heroku等のPostgresやJawsを使う手もあるが、あまりにも容量が少なすぎる。&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebaseへの期待が高まりすぎる
&lt;/h2&gt;

&lt;p&gt;RDBはメリットが高いが、やはりサーバーが不要というFirebaseのデータベースのメリットも同様に非常に高い。そのため、いつかバージョンアップした際にクエリが強くなるのではないか、もっとデータが取りやすくなるのではないか、みたいな期待が高まりすぎてしまった。&lt;/p&gt;

&lt;p&gt;多分Datastoreを流用していると思うので、あまり期待しすぎても仕方なく、ひとまず現在は現状のままどうやって使っていくかを考える必要がある。Firebaseのデータベースを使うとなると、それでうまく作り方を考える必要が出てくる。（これはこれで面白いのだが）&lt;/p&gt;

&lt;h2&gt;
  
  
  どちらかに限定する必要はない
&lt;/h2&gt;

&lt;p&gt;そこで気づいたのが、そもそも元々RDBを使っているアプリケーションであっても、Firebaseのデータベースを併用して使えばもっと便利になるのでは、ということ。&lt;/p&gt;

&lt;p&gt;例えばこのCrieitの例だが、先日リアルタイムで下書きをライブ公開できる機能を作成した。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://crieit.net/posts/634be945aec88661d0edd1656e25f588" rel="noopener noreferrer"&gt;編集中の内容をライブ公開できるリアルタイム投稿機能を実装しました&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このサイトはGoogle Compute Engineの永久無料枠で使えるf1-microというインスタンスで動いているのだが、とにかくメモリの使用量も小さく貧弱。Laravelは一応Pusher等と連携してWebSocketを使える機能があるのでLaravel自体でもライブ公開機能は実現可能なのだが、結局状態変化があった時にデータを取得などしようとするとLaravel自体にアクセスが行ってしまう。あまりに頻度が多いと、現在のサーバーのスペックでは厳しくなる可能性がある。&lt;/p&gt;

&lt;p&gt;その時に思い出したのがFirebaseのRealtime Databse。これは同時接続数などの制限があるものの、書き込み、読み込み等の回数には特に制限がない。つまり、制限内でライブ公開する機能を付けるのであれば、サーバーの負荷ゼロでライブ公開機能が実現できてしまう。&lt;/p&gt;

&lt;p&gt;実際に下書き入力中の内容は特にサーバーに保存する必要がないので、入力した内容はそのままRealtime Databaseに保存し、閲覧者側はそれを受け取るだけになっており、完全にサーバー負荷も費用的な負荷もなくライブ公開の機能が作れてしまった。無料で運営している個人開発サービスとしてはこのやり方はとても相性が良い。流行りすぎて人が増えるとそうはいかなくなるが、とりあえず今の段階では全く心配はない。むしろ遊び道具が一つ増えて楽しみが増えた。&lt;/p&gt;

&lt;p&gt;逆で、Firebaseだけで運用しているサービスにRDBの利用を追加する、ということも考えられる。RDSやCloud SQLでは費用が増えてしまうが、とりあえずHerokuのPostgres等でも良いかもしれない。Firestore側が苦手とする集計や柔軟なページングなどを行いたいデータだけ、併用して保存することで、Cloud Functions経由で簡単に集計データなどが取れたりするのではないかと思う。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;とにかく柔軟に考えて、併用することでパフォーマンス的にも費用的にもベストな形になるような状態を目指していくと面白い。&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>firebase</category>
      <category>firestore</category>
      <category>japanese</category>
    </item>
    <item>
      <title>CakePHP4が出るらしいので各バージョンの歴史を振り返る</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Sat, 24 Nov 2018 13:19:13 +0000</pubDate>
      <link>https://dev.to/dala00/cakephp4-3jfd</link>
      <guid>https://dev.to/dala00/cakephp4-3jfd</guid>
      <description>&lt;p&gt;CakePHP4がリリースされる日がもうすぐ近づいているようです。僕は今は使っていませんが、バージョン１の時代から３まで使い続けていたのでせっかくなので今までのバージョンを振り返ってみました。&lt;/p&gt;

&lt;h2&gt;
  
  
  CakePHP1
&lt;/h2&gt;

&lt;p&gt;CRUDを簡単に構築できるスクリーンキャストが印象的で、見た瞬間に一気にのめり込んで大好きになったきかっけのフレームワークでした。当時いくつかPHPのフレームワークはありましたが、どれもぱっとせず、満足できるほど使いやすい、というものではありませんでした。CakePHPはその中でようやく使いやすいものがでてきた、という印象がありました。&lt;/p&gt;

&lt;p&gt;Ruby on Railsを参考に作った（？）ということだったと思いますが、その甲斐もありかなり便利でその後の色々なフレームワークに影響を与えていたと思います。&lt;/p&gt;

&lt;h3&gt;
  
  
  コードの自動生成
&lt;/h3&gt;

&lt;p&gt;bakeコマンドを使って色々自動生成してくれたため、それを修正して使ったりすることでかなり開発が早かったです。&lt;/p&gt;

&lt;h3&gt;
  
  
  分かりやすいファイル構成
&lt;/h3&gt;

&lt;p&gt;MVCのベースとなる各ファイル自体もわかりやすかったですし、他の機能を拡張するためのコンポーネントやヘルパーなどもわかりやすく、簡単にファイル分割ができ開発はしやすかったです。&lt;/p&gt;

&lt;h3&gt;
  
  
  細かいバージョン
&lt;/h3&gt;

&lt;p&gt;たしかCakePHP1は1.1, 1.2, 1.3という細かいバージョンでちょっと大きな機能追加があったりなどした記憶があります。（認証コンポーネントの追加やビヘイビアなど）&lt;/p&gt;

&lt;h2&gt;
  
  
  CakePHP2
&lt;/h2&gt;

&lt;p&gt;PHP4の対応を捨てたバージョンです。結構色々とガラッと変わり、CakePHP1のプロジェクトはそのままでは動きませんでした。&lt;/p&gt;

&lt;p&gt;今現在もかなり使われているような印象があります。baserCMSなんかもたしかこのバージョンだったと思います。１とどういう違いがあったのかは忘れてしまいましたが、何にしろ色々とちょっとずつ便利になっていたような記憶があります。これぞ、というものがあったわけではなく、１が色々検討しながら開発されたものだと思うので、色々話がまとまってきっちりした、という感じだったような気がします。&lt;/p&gt;

&lt;h2&gt;
  
  
  CakePHP3
&lt;/h2&gt;

&lt;p&gt;Composerが導入され、モダンなフレームワークとなりました。ここでもあらゆることがガラッと変わり、CakePHP2のプロジェクトはそのまま使うことはできませんでした。多分現在CakePHP2のプロジェクトが残っているはそのあたりが原因だと思われます。&lt;/p&gt;

&lt;p&gt;正直ここでかなり使いやすくなったので、3が使えるようになったら2を使う必要はないと思われます。とはいえ業務上アップグレードできない状況や、Composer導入や色々な仕様変更等の影響で踏み出せない勢の影響でまだまだ残っている様な印象を受けます。可能であれば3を使っていきましょう。&lt;/p&gt;

&lt;p&gt;ちなみにこの時知りましたが、一応バージョンアップ対応用のスクリプトみたいなのを公式が作ってるんですよね。でもたいしたことはしてくれないのでほとんど意味がありませんでした。&lt;/p&gt;

&lt;h2&gt;
  
  
  CakePHP4
&lt;/h2&gt;

&lt;p&gt;PHP7の対応となりました。ただ今回はそのあたりの対応がメインっぽく、それが要因となる破壊的変更以外はあまり行われず3からのアップグレードがわりと容易になるようです。軽いアップグレードが可能なのはここが初ですね。できる人はどんどんやっていった方が良いのではないかと思います。PHP5のサポートも今年で終わりですし。&lt;/p&gt;

</description>
      <category>cakephp</category>
      <category>php</category>
      <category>japanese</category>
    </item>
    <item>
      <title>開発時はSQLのログを見よう</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Mon, 19 Nov 2018 13:44:55 +0000</pubDate>
      <link>https://dev.to/dala00/sql-89o</link>
      <guid>https://dev.to/dala00/sql-89o</guid>
      <description>&lt;p&gt;最近はSQLを利用したアプリケーションを開発する際、ほとんどORMを使うのが当たり前になってきていると思います。しかし実際に実行されるクエリを確認しないと大変なことになることもあるため、開発時のログをある程度は見ながら進めた方が良いと思います。&lt;/p&gt;

&lt;h2&gt;
  
  
  実行したSQLを見る方法
&lt;/h2&gt;

&lt;p&gt;例えばPHPのフレームワークの場合、LaravelであればDebugbar、CakePHPであればDebugKitを導入することで、ページを開いた際に実行されたSQLの一覧を見たりすることができます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_5OyWE6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yf12slakvo3r5nilrrau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_5OyWE6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yf12slakvo3r5nilrrau.png" alt="" width="880" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;その他の言語やフレームワーク、例えばRuby on RailsやPhoenix等は、開発サーバーを実行している際にSQLの履歴が流れてくるためそれによって確認することができます。&lt;/p&gt;

&lt;h2&gt;
  
  
  実際に何を見たら良いのか
&lt;/h2&gt;

&lt;p&gt;基本的に見たほうが良いところというのは、重いSQLではないかと、SQLの実行回数あたりになると思います。&lt;/p&gt;

&lt;p&gt;普通のシンプルなSQLであればだいだい問題ないとは思いますが、JOINしていたりサブクエリを含んでいたり関数を使っていたりする場合、そのSQLのEXPLAINで確認してちゃんとインデックスが効いているかなどを見たほうが良いです。ただ、このあたりはスキーマを検討する際にだいたい考えられていたりすることもあるので、大丈夫なことも多いかもしれません。&lt;/p&gt;

&lt;p&gt;もう一つ問題となるのが、実行回数です。以前僕が既存のプロジェクトに途中から参加したことがあったのですが、一ページ内で3000以上のクエリを実行しているページがいくつもありました。たしかカレンダーとか、データ一覧のそれぞれのデータに対してループを行い、更に取得したデータに対してもループしてSELECTを実行する、みたいな感じでそれくらいになってしまっていたと思います。&lt;/p&gt;

&lt;p&gt;なんでこんな事になっていたのかと言うと、それはLaravelのプロジェクトだったのですが、結局Debugbarを入れていなかったためクエリの実行回数が可視化されておらず、作成した本人も全く気づいていなかったような状態でした。後で僕がDebugbarを入れたため、そのページを触った時に気づきました。&lt;/p&gt;

&lt;p&gt;この場合の解決法としては、ループの中でSELECTを実行せず、最初に1回必要なデータを全部取得しておき、ループの中ではそれを参照するだけにする、という形にすればほとんどSELECTを実行する必要がなくなります。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;今もしSQLを利用しているプロジェクトで実行しているSQLを全く見ていない、という場合、ちょろっと見てみると良いと思います。&lt;/p&gt;

&lt;p&gt;特に炎上中のプロジェクトの場合、誰もそのあたり考える余裕もなく適当に作られている場合があるため、怖いもの見たさで可視化してみるとよいかもしれませんよ…。&lt;/p&gt;

</description>
      <category>sql</category>
    </item>
    <item>
      <title>LaravelのHTTPテストでoutput buffersが閉じられていないエラー</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Wed, 14 Nov 2018 11:29:16 +0000</pubDate>
      <link>https://dev.to/dala00/laravelhttpoutput-buffers-408d</link>
      <guid>https://dev.to/dala00/laravelhttpoutput-buffers-408d</guid>
      <description>&lt;p&gt;Laravelのテストでいつの間にかRiskyテストが出るようになっていた。失敗ではないのでCIでも何も言われなかったようで、実行時にもエラーが発生していなかったので気づかずにいたらしい。&lt;/p&gt;

&lt;p&gt;具体的には下記のようなエラー。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Test code or tested code did not &lt;span class="o"&gt;(&lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt; close its own output buffers  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;結論としては、下記のようなテンプレートの書き方をしていたために正常にレスポンスが返っていなかったらしい。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@section('additionallTag', $data-&amp;gt;tag)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これは特に記述自体は問題ないと思うが、変数の中に入っているのがHTMLタグだったため今回のエラーが発生しているっぽい。そのため下記のように修正したところ改善した。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@section('additionalTag')  
{!! $data-&amp;gt;tag !!}  
@endsection  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;同じエラーの改善方法を解説している記事も見つけた。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qiita.com/t_k_m_s_otya/items/6e973f90c09df40f2224"&gt;LaravelのHTTPテストでなぜかresponseが返ってこない件&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;この場合はsectionが閉じられていないためとのことだが、今回のように変数の指定によってはうまく閉じられなくなってしまうということっぽい。&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>phpunit</category>
    </item>
    <item>
      <title>Firebaseの匿名認証はなんのためにあるのか - セキュリティ編</title>
      <dc:creator>dala00</dc:creator>
      <pubDate>Fri, 19 Oct 2018 11:58:34 +0000</pubDate>
      <link>https://dev.to/dala00/firebase----22m8</link>
      <guid>https://dev.to/dala00/firebase----22m8</guid>
      <description>&lt;p&gt;FirebaseのAuthenticationはGoogleログインやTwitterログインが簡単にできる便利なものですが、その中には匿名認証というものもあります。これが一体何のためにあるのか、セキュリティの観点から見てみます。今回はブラウザ上で動くWebアプリケーションの話です。&lt;/p&gt;

&lt;h2&gt;
  
  
  そもそも匿名認証とは？
&lt;/h2&gt;

&lt;p&gt;匿名認証というのは、ユーザーが何も操作などをしなくても、内部で勝手にログインする機能です。&lt;/p&gt;

&lt;h2&gt;
  
  
  何のためにあるのか？
&lt;/h2&gt;

&lt;p&gt;基本的には公式の説明にいきなり書かれています。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/auth/web/anonymous-auth?hl=ja"&gt;JavaScript を使用して Firebase 匿名認証を行う  |  Firebase&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;アプリに登録していないユーザーが、セキュリティ ルールで保護されているデータを使用できるようになります。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  セキュリティとは？
&lt;/h2&gt;

&lt;p&gt;Firebaseは便利ですが、怖いところがあります。Webアプリケーションの場合、JavaScriptを使うためFirebaseアプリケーションを初期化するための設定情報が丸見えになってしまいます。そのため、何のセキュリティも導入していないとその設定情報を使って誰でも勝手にそのアプリケーションの情報を読み書きできてしまいます。&lt;/p&gt;

&lt;p&gt;それを制限する方法として、ForestoreやStorageにはセキュリティルールがあります。例えば以下のようなことが設定できます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;あるデータは誰でも閲覧できる&lt;/li&gt;
&lt;li&gt;あるデータはログインしたユーザーしか書き込みできない&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;等。&lt;/p&gt;

&lt;h3&gt;
  
  
  セキュリティルールを設定しないとどうなるのか？
&lt;/h3&gt;

&lt;p&gt;前述の通り設定情報が丸見えですので、それをコピーして誰かが自分のプログラムに組み込めば、他人のFirebaseのアカウントで自由にデータなどを扱えるようになってしまいます。&lt;/p&gt;

&lt;p&gt;見られてはいけないデータが見られてしまいますし、そもそも利用量によって金額も増えてしまう恐れがありとても危険です。&lt;/p&gt;

&lt;h2&gt;
  
  
  匿名認証を使ったセキュリティ設定の一例
&lt;/h2&gt;

&lt;p&gt;セキュリティ上問題ないと思われる、匿名認証を使ったシンプルな例をあげてみます。&lt;/p&gt;

&lt;h3&gt;
  
  
  全てのデータの読み書きを認証済みの場合のみ許可する
&lt;/h3&gt;

&lt;p&gt;認証していない場合はあらゆるデータの読み書きを不可能にします。&lt;/p&gt;

&lt;h3&gt;
  
  
  匿名認証で誰もが必ず認証している状態にする
&lt;/h3&gt;

&lt;p&gt;これで、ログインしなくても全てのユーザーが読み書きできるようになります。&lt;/p&gt;

&lt;h3&gt;
  
  
  ログイン可能なホスト名を本番環境のみにする
&lt;/h3&gt;

&lt;p&gt;Firebase ConsoleのAuthenticationの設定でログインできるホスト名を設定することができます。ここで本番のホスト名だけを認証可能にします。こうすることで、Firebaseの設定情報を使われてもデータにアクセスすることなどは不可能となります。&lt;/p&gt;

&lt;p&gt;localhost等も当然消しておきましょう。開発時には別途開発用のFirebaseアプリケーションを用意し、そちらの設定を使ってください。ビルド時にはcross-envを使うと良いと思います。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;このように、匿名認証を入れることで本番でしかデータを扱うことができなくなり、セキュリティのベース部分の強化を行うことができます。&lt;/p&gt;

&lt;p&gt;とにかく、設定情報が丸見えというのは非常に恐ろしい話です。当記事の話も鵜呑みにせず、しっかりと色々なパターンを考えセキュリティを強化しておきましょう。（むしろ間違っている話があればご指摘をお願いします）&lt;/p&gt;

&lt;p&gt;Firebaseと連携するあらゆる処理をfunctionsに実装して設定情報は公開しないようにする、くらいの強固さで考えるくらいでも良いかもしれません。&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>firestore</category>
      <category>authentication</category>
    </item>
  </channel>
</rss>
