2020年06月26日

Firestoreで一括で更新する

firestore.batch()を利用すると一括で更新することができる。 batch.update()で変更するリファレンスと変更するフィールドを指定してbatch.commit();を利用して一括で更新することが可能


const changeFinishedAll = useCallback(async () => {
const batch = firestore.batch();
const todoListRef = await firestore.collection("todo").get();
todoListRef.docs.forEach((todo) => {
batch.update(todo.ref, {
finished: true,
});
});
await batch.commit();
getData();
}, []);
posted by ねこまんま at 00:29 | Firebase | 更新情報をチェックする

Firestoreのリアルタイム・リスナー

Firebase☓ReactでTODOリスト作成(Read)でFirestoreから情報を取得して表示する方法を説明したけどリアルタイム・リスナーを利用するとクライントのデータとサーバーのデータを同期させることができる。

onSnapshot()でサーバーの情報を監視でき変更が発生すると実行がされるのでその中でSnapshotを展開することでサーバーとクライアントのでーたを同期できる


useEffect(() => {
const getBinders = async () => {
firestore
.collection("binder")
.orderBy("createdAt", "desc")
.onSnapshot((snapshot) => {
const binders = [];
snapshot.forEach((doc) => {
binders.push({
...doc.data(),
id: doc.id,
});
});
changeBinders(binders);
});
};
getBinders();
}, []);
posted by ねこまんま at 00:00 | Firebase | 更新情報をチェックする

2020年06月17日

Firebaseで「お使いの Cloud Firestore データベースはクライアントからのリクエストを拒否しています。セキュリティ ルールをアップデートするまでこの状態が続きます」とエラーがでた場合。

firestore.rulesが適切でない場合このようなエラーがでる。

開発初期は期間限定で許可されるルールだが一定期間たったらこのようなエラーがでる。

期間を延長したい場合はfirestore.rulesのtimestamp.date()の日付を変更して、

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.time < timestamp.date(2020, 6, 30);
}
}
}


以下のコマンドを利用して開発期間を延長する

firebase deploy --only firestore:rules
posted by ねこまんま at 01:46 | Firebase | 更新情報をチェックする

2020年04月21日

Firebaseをローカルで実行

まずはシンプルなhttpリクエストのエミューレーションから

functions/index.jsを以下のように変更して

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require("express");
const cors = require('cors');

admin.initializeApp({
credential: admin.credential.applicationDefault()
});

const app = express();
app.use(cors({ origin: true }));

app.get('/helloworld',(req, res) => {
res.send('hello world');
})

const api = functions.https.onRequest(app);
module.exports = { api };


以下のコマンドでエミュレーターを実行

firebase emulators:start --only functions


表示されたURLにルーティングを付けて、例えば以下のURLにアクセスすると「hello world」と表示されるようになります。

http://localhost:5001/react-todo-23957/us-central1/api/helloworld

Firestoreもエミュレートしたい場合は以下のコマンドで実行するだけ

firebase emulators:start


思ったよりかんたんにできてすごい

参考:ローカルでの関数の実行  |  Firebase
posted by ねこまんま at 09:40 | Firebase | 更新情報をチェックする

2020年04月18日

Firebase☓ReactでTODOリスト作成(Delete)

Firebase☓ReactでTODOリスト作成(Express)の続き

Express化したので細かいハンドリングで悩む必要がなくなったので以下をfunctions/index.jsに追加

app.delete('/todo/:id', async (req, res) => {
const newTodo = db.collection("todos").doc(req.params.id)
await newTodo.delete()
res.send("ok3");
})


React部分は以下のように変更すると削除が実装できる

function App() {
const [mode,changeMode] = useState("list");
const [editId,changeEditId] = useState(null);
const [todos,changeTodos] = useState([]);
const [title,changeTitle] = useState("");
const [comment,changeComment] = useState("");
useEffect(()=>{
fetchResponse()
},[])

// 一覧取得
const fetchResponse = async () => {
const res = await axios.get(PATH + 'todos')
changeTodos(res.data);
}

// 新規追加
const addTodo = async () => {
await axios.post(PATH + 'todo',{
title,
comment,
checked:false
})
fetchResponse()
changeTodos([])
changeTitle("")
changeComment("")
}

// 編集
const editTodo = async () => {
await axios.put(PATH + 'todo/' +editId ,{
title,
comment,
checked:false
})
fetchResponse()
changeTodos([])
changeTitle("")
changeComment("")
changeMode("list")
}

// 削除
const deleteTodo = async (id) => {
await axios.delete(PATH + 'todo/'+ id)
fetchResponse()
changeTodos([])
}

const changeEditMode = (id) =>{
changeMode("edit")
changeEditId(id)
const todo = todos.find(todo => todo.id===id)
changeTitle(todo.title)
changeComment(todo.comment)
}

return (
<div className="App">
{mode === 'list' && (
<div>
title:<input type="text" value={title} onChange={e => changeTitle(e.currentTarget.value)} /><br />
comment:<input type="text" value={comment} onChange={e => changeComment(e.currentTarget.value)} /><br />
<button onClick={() => addTodo()}>add</button><br />
<ul>
{todos && todos.map((todo)=>(
<li key={todo.id}>
<input type="button" value="edit" onClick={()=> changeEditMode(todo.id)}/ >
<input type="button" value="delte" onClick={()=> deleteTodo(todo.id)}/ >
{todo.title}
</li>
))}
</ul>
</div>
)}
{mode === 'edit' && (
<div>
title:<input type="text" value={title} onChange={e => changeTitle(e.currentTarget.value)} /><br />
comment:<input type="text" value={comment} onChange={e => changeComment(e.currentTarget.value)} /><br />
<button onClick={() => editTodo()}>edit</button><br />
</div>
)}
</div>
);
}
posted by ねこまんま at 17:04 | Firebase | 更新情報をチェックする

Firebase☓ReactでTODOリスト作成(Express)

Firebase☓ReactでTODOリスト作成(Update)の続き

Deleteメソッドの実装でハマったので音を上げてExpress化

functions/package.jsonのenginesを10に変更して

  "engines": {
"node": "10"
},


node10をインストールしてfunctions内でExpressをインストール

cd functions/
ndenv install 10.20.1
ndenv local 10.20.1
yarn add express


エンジンの変更に合わせてルーティングも調整

一覧取得 GET https://.../api/todos
新規追加 POST https://.../api/todo
編集 PUT https://.../api/todo/:id

functions/index.jsは以下のようになります

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require("express");
const cors = require('cors');

admin.initializeApp({
credential: admin.credential.applicationDefault()
});

const db = admin.firestore();

const app = express();
app.use(cors({ origin: true }));

app.get('/todos', async (req, res) => {
const todosSnapshot = await db.collection('todos').orderBy("createdAt", "desc").get();
const todos = [];
todosSnapshot.forEach(doc => {
todos.push({
...doc.data(),
id: doc.id,
});
});
res.send(JSON.stringify(todos));
});
app.post('/todo', async (req, res) => {
const todo = {
'title': req.body.title,
'comment': req.body.comment,
'checked': false,
'createdAt': new Date()
};
const newTodo = db.collection("todos").doc()
await newTodo.set(todo)
res.send("ok1");
})
app.put('/todo/:id', async (req, res) => {
const todo = {
'title': req.body.title,
'comment': req.body.comment,
'checked': false,
};
const newTodo = db.collection("todos").doc(req.params.id)
await newTodo.update(todo)
res.send("ok2");
})

const api = functions.https.onRequest(app);
module.exports = { api };
posted by ねこまんま at 16:59 | Firebase | 更新情報をチェックする

2020年04月16日

Firebase☓ReactでTODOリスト作成(Update)

Firebase☓ReactでTODOリスト作成(Sort)の続き

POSTで追加だったのでPUTの場合に更新されるように変更

exports.post= functions.https.onRequest(async (request, response) => {
return cors()(request, response, () => {
if(request.method === "POST"){
const todo = {
'title': request.body.title,
'comment': request.body.comment,
'checked': false,
'createdAt': new Date()
};
const newTodo = db.collection("todos").doc()
newTodo.set(todo)
response.send("ok1");
}
if(request.method === "PUT"){
const todo = {
'title': request.body.title,
'comment': request.body.comment,
'checked': false,
};
const newTodo = db.collection("todos").doc(request.body.id)
newTodo.update(todo)
response.send("ok2");
}
})
});


React部分は以下のようになってる。擬似的に編集ページをつくったので長め

function App() {
const [mode,changeMode] = useState("list");
const [editId,changeEditId] = useState(null);
const [todos,changeTodos] = useState([]);
const [title,changeTitle] = useState("");
const [comment,changeComment] = useState("");
useEffect(()=>{
fetchResponse()
},[])

const fetchResponse = async () => {
const res = await axios.get(PATH + 'list')
changeTodos(res.data);
}

// 新規追加
const addTodo = async () => {

await axios.post(PATH + 'post',{
title,
comment,
checked:false
})
fetchResponse()
changeTodos([])
changeTitle("")
changeComment("")
}

const editTodo = async () => {
await axios.put(PATH + 'post',{
id:editId,
title,
comment,
checked:false
})
fetchResponse()
changeTodos([])
changeTitle("")
changeComment("")
changeMode("list")
}

const changeEditMode = (id) =>{
changeMode("edit")
changeEditId(id)
const todo = todos.find(todo => todo.id===id)
changeTitle(todo.title)
changeComment(todo.comment)
}

return (
<div className="App">
{mode === 'list' && (
<div>
title:<input type="text" value={title} onChange={e => changeTitle(e.currentTarget.value)} /><br />
comment:<input type="text" value={comment} onChange={e => changeComment(e.currentTarget.value)} /><br />
<button onClick={() => addTodo()}>add</button><br />
<ul>
{todos && todos.map((todo)=>(
<li key={todo.id}>
<input type="button" value="edit" onClick={()=> changeEditMode(todo.id)}/ >
{todo.title}
</li>
))}
</ul>
</div>
)}
{mode === 'edit' && (
<div>
title:<input type="text" value={title} onChange={e => changeTitle(e.currentTarget.value)} /><br />
comment:<input type="text" value={comment} onChange={e => changeComment(e.currentTarget.value)} /><br />
<button onClick={() => editTodo()}>edit</button><br />
</div>
)}
</div>
);
}
posted by ねこまんま at 22:40 | Firebase | 更新情報をチェックする

2020年04月15日

Firebase☓ReactでTODOリスト作成(Sort)

Firebase☓ReactでTODOリスト作成(Create)の続き

登録順に表示したい。デフォルトではID順になってしまうらしい。

登録時にcreatedAtを追加して

    const todo = {
'title': request.body.title,
'comment': request.body.comment,
'checked': false,
'createdAt': new Date()
};


取得時に並び替えてあげるとソートして取得ができる

const todosSnapshot = await db.collection('todos').orderBy("createdAt", "desc").get();
posted by ねこまんま at 01:18 | Firebase | 更新情報をチェックする

Firebase☓ReactでTODOリスト作成(Create)

Firebase☓ReactでTODOリスト作成(Read)の続き

POSTのCORSがうまく設定できずに悪戦苦闘
Content-Typeがapplication/x-www-form-urlencodedの場合はうまく言ったのだけどapplication/jsonの場合はCORSのエラーが出てしまう。OPTIONSリクエストの調整がうまくできていないのだろうか。。。

とはいえ検証に時間がかけても解決できないのでパッケージで解決

const cors = require('cors');

exports.post= functions.https.onRequest(async (request, response) => {
return cors()(request, response, () => {
const todo = {
'title': request.body.title,
'comment': request.body.comment,
'checked': false
};
const newTodo = db.collection("todos").doc()
newTodo.set(todo)
response.send("ok1");
})
});


とすると登録ができるようになりました。React側のコードはこれ

function App() {
const [todos,changeTodos] = useState([]);
const [title,changeTitle] = useState("");
const [comment,changeComment] = useState("");
useEffect(()=>{
fetchResponse()
},[])

const fetchResponse = async () => {
const res = await axios.get(PATH + 'list')
changeTodos(res.data);
}

// 新規追加
const addTodo = async () => {
await axios.post(PATH + 'post',{
title,
comment,
checked:false
})
fetchResponse()
changeTodos("")
changeTitle("")
}

return (
<div className="App">
title:<input type="text" value={title} onChange={e => changeTitle(e.currentTarget.value)} /><br />
comment:<input type="text" value={comment} onChange={e => changeComment(e.currentTarget.value)} /><br />
<button onClick={() => addTodo()}>add</button><br />
<ul>
{todos && todos.map((todo,i)=>(
<li key={i}>{todo.title}</li>
))}
</ul>
</div>
);
}


このままだと登録後に取得できるリストが順不同になってしまう
posted by ねこまんま at 00:59 | Firebase | 更新情報をチェックする

2020年04月10日

Firebase☓ReactでTODOリスト作成(Read)

Firebase☓ReactでTODOリスト作成の続き

functions/index.jsを変更してAPIを/getから/listに変更して、todoオブジェクトを平坦化、ローカルのReact AppからアクセスできるようにCROSSの設定を追加します。

exports.list= functions.https.onRequest(async (request, response) => {
const todosSnapshot = await db.collection('todos').get();
const todos = [];
todosSnapshot.forEach(doc => {
todos.push({
...doc.data(),
id: doc.id,
});
});
response.set('Access-Control-Allow-Origin', 'http://localhost:3000'); // localhostを許可
response.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS, POST'); // DELETEだけは拒否
response.set('Access-Control-Allow-Headers', 'Content-Type'); // Content-Typeのみを許可
response.send(JSON.stringify(todos));
});


src/App.jsを変更して上で設定したAPIにアクセスして一覧を表示するように変更します。

import React, { useState , useEffect } from 'react';

function App() {
const [todos,changeTodos] = useState([]);
useEffect(()=>{
fetchResponse()
},[])

const fetchResponse = () => {
fetch('https://us-central1-react-todo-23957.cloudfunctions.net/list')
.then( res => res.json() )
.then( res => {
changeTodos(res);
})
}

return (
<div className="App">
<ul>
{todos && todos.map((todo,i)=>(
<li key={i}>{todo.title}</li>
))}
</ul>
</div>
);
}

export default App;

posted by ねこまんま at 11:44 | Firebase | 更新情報をチェックする

Firebase☓ReactでTODOリスト作成


npm install -g create-react-app
create-react-app firbase-todo
cd firbase-todo
yarn start


https://console.firebase.google.com/?hl=ja&pli=1にアクセスして「react-todo」プロジェクトを作成
このプロジェクトで Google アナリティクスを有効にするはOFFで、
アプリに Firebase を追加して利用を開始しましょうからWebを追加
アプリのニックネームは「react-todo」としてFirebaseHostingも有効にしてアプリを登録
設定の「Google Cloud Platform(GCP)リソース ロケーション」をasia-northeast1(東京)を設定
データベースに移動してネイティブモードに

npm install -g firebase-tools
firebase login
firebase init


Firestore とFunctionsとHosting 選択
Use an existing projectを選択
先ほど作成したプロジェクト「react-todo」を選択
What file should be used for Firestore Rules? Enter
What file should be used for Firestore indexes? Enter
What language would you like to use to write Cloud Functions? JavaScript
Do you want to use ESLint to catch probable bugs and enforce style? No
Do you want to install dependencies with npm now? Yes
? What do you want to use as your public directory? build
? Configure as a single-page app (rewrite all urls to /index.html)? No
yarn build
serve -s build でローカルが立ち上がる
firebase deploy --only hosting でホスティングにアップされる


functions/index.jsの以下のコメントアウトを外す

exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});


以下でデプロイ

firebase deploy --only functions

表示されたURLを叩くと「Hello from Firebase!」と表示される

データベースのColllectionを作成しておく

スクリーンショット 2020-04-10 0.08.33.png

Firestoreにアクセスできるように firebase-adminをインストールしておく

yarn add firebase-admin


Firestoreアクセス用にindex.jsを以下のスクリプトに変更してデプロイ、ブラウザでgetにアクセスするとFiresoreの内容が出力される

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp({
credential: admin.credential.applicationDefault()
});

const db = admin.firestore();

exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});

exports.get= functions.https.onRequest(async (request, response) => {
const todosSnapshot = await db.collection('todos').get();
const todos = [];
todosSnapshot.forEach(doc => {
todos.push({
id: doc.id,
data: doc.data()
});
});
response.send(JSON.stringify(todos));
});

posted by ねこまんま at 00:11 | Firebase | 更新情報をチェックする

2020年02月24日

FirebaseのgetApplicationDefaultAsyncエラー

以下のコマンドでFirebase Functionsをlocalで起動するとエラーになる

GOOGLE_APPLICATION_CREDENTIALS=./src/xxxx-firebase-adminsdk.json firebase serve --only functions


以下のエラー内容

Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information. at GoogleAuth.getApplicationDefaultAsync


以下のコマンドを入力後表示されるURLでブラウザでログインし直したら承認がうまくいきまいした

gcloud auth application-default login
posted by ねこまんま at 12:42 | Firebase | 更新情報をチェックする

2019年01月14日

FirebaseHostingを利用してホスティング環境をデプロイする

Firebaseでプロジェクトを作成して左メニューからHostingを有効にしておく、あとはコマンドラインから。

まずはFirebas toolsをインストールしておく

npm install -g firebase-tools


まずはログイン

firebase login


以下のように聞かれるのでyesを選択

? Allow Firebase to collect anonymous CLI usage and error report
ing information?


ブラウザが開くのでFirebaseを利用しているGoogleのアカウントを選択しよう。

firebase init


ここでhostingを選択して、Firebaseでプロジェクトが選択できる場合は選択、選択できない場合は[don't setup a default project]を選んでおく ( あとでFirebase use −addコマンドで追加できる

デプロイするディレクトリとすべてのURLのエイリアスを/index.htmlにするSPAモードの選択ができる

? What do you want to use as your public directory? 
? Configure as a single-page app (rewrite all urls to /index.htm
l)?


これで、firebase.jsonファイルと.firebasercファイルが作成される

あとは以下のコマンドでデプロイできるはず

firebase deploy





posted by ねこまんま at 16:29 | Firebase | 更新情報をチェックする

2019年01月12日

FirebaseとNuxt.jsを使ってユーザ認証関係2

FirebaseとNuxt.jsを使ってユーザ認証関係の続き。

前回はメールアドレスとパスワードを入力してアカウントを作成したが、今回はメールアドレス入力→確認メール→認証画面というフローでユーザ認証ができるように変更していきます。
posted by ねこまんま at 13:22 | Firebase | 更新情報をチェックする

2018年12月14日

FirebaseとNuxt.jsを使ってユーザ認証関係

via: FirebaseとNuxt.jsを使ってユーザ認証関係を簡単に作ってみる

経験が少ない技術のキャッチアップがてらにサンプルをひたすら写経シリーズ。

初期設定



npx create-nuxt-app nuxt_firebase


でインストールして以下のコマンドで実行

cd nuxt_firebase
npm run dev


これで http://localhost:3000/ にアクセスできます。楽

Firebaseをインストールして

npm install firebase


pluginsの中にfirebase.jsを作成

import firebase from 'firebase'

if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: "API_KEY",
authDomain: "APP_ID.firebaseapp.com",
databaseURL: "https://PROJECT_ID.firebaseio.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "1234567890"
})
}

export default firebase


各種の設定はFirebaseプロジェクトの「ウェブアプリに Firebase を追加」から取得できる。

また、左メニューのAuthenticationよりログイン方法のメール / パスワードを有効にして、サンプルユーザーを作成しておく

ストア設定



export const strict = false

export const state = () => ({
user: null,
})

export const mutations = {
setUser (state, payload) {
state.user = payload
}
}

export const actions = {
setUser ({ commit }, payload) {
commit('setUser', payload)
}
}

export const getters = {
isAuthenticated (state) {
return !!state.user
}
}



ログイン機能




/pages/index.vueを以下のように変更しておく

<template>
<div>
<!-- ログイン中に表示される画面 -->
<div v-if="isAuthenticated">
{{ user.email }}でログイン中です<br>
<button v-on:click="logout">ログアウト</button><br>
<a href="/member-page">メンバーページへ</a>
</div>
<!-- ログインしていない時に表示される画面 -->
<div v-else>
メール<br>
<input type="text" v-model="email"><br>
パスワード<br>
<input type="password" v-model="password"><br>
<button v-on:click="login">ログイン</button>
</div>
</div>
</template>

<script>
import firebase from '~/plugins/firebase'
import { mapActions, mapState, mapGetters } from 'vuex'
export default {
data() {
return {
email: '',
password: ''
}
},
computed: {
...mapState(['user']),
...mapGetters(['isAuthenticated'])
},
mounted() {
firebase.auth().onAuthStateChanged((user) => {
this.setUser(user)
})
},
methods : {
...mapActions(['setUser']),
login() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password)
.then(user => {
// ログインしたら飛ぶページを指定
// this.$router.push("/member-page")
}).catch((error) => {
alert(error)
});
},
logout() {
firebase.auth().signOut()
.then(() => {
this.setUser(null)
}).catch((error) => {
alert(error)
})
}
}
}
</script>


これによりログイン時と未ログイン時に状況を切り替えることができる。

ここまでは参考サイトのほぼ引用。
ここからは独自の内容。

アカウント作成



index.vueにアカウント作成用にページへのURLを追加して

      <button v-on:click="login">ログイン</button>
<p><a href="/register-user">新規会員登録</a></p>
</div>
</div>
</template>


/pages/register-user.vueを以下のように設定する

<template>
<div>
メール<br>
<input type="text" v-model="email"><br>
パスワード<br>
<input type="password" v-model="password"><br>
<p v-if="error_code==='auth/invalid-email'">メールアドレスが不正です</p>
<button v-on:click="registerUser">登録</button>
</div>
</template>

<script>
import firebase from '~/plugins/firebase'
import { mapActions } from 'vuex'
export default {
data() {
return {
email: '',
password: '',
error_code:''
}
},
methods : {
...mapActions(['setUser']),
registerUser() {
firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
this.setUser({
email:this.email,
password:this.password
})
this.$router.push("/")
})
.catch(error =>{
this.error_code = error.code
});
}
}
}
</script>


これでアカウント作成成功時にはストアにアカウント情報を登録してindexページに遷移、入力内容が不正な場合はエラーをヒュウ視することができる
posted by ねこまんま at 11:54 | Firebase | 更新情報をチェックする