firebase创建数据库
I had the need to create a storage for some data for my membership Club, the place where I teach programming.
我需要为会员俱乐部 (我在那儿学习编程)创建一些数据的存储。
I wanted my users to be able to manually say “I completed this course”, by clicking a button.
我希望我的用户能够通过单击按钮手动说“我完成了本课程”。
Basically, I wanted to store a particular object for every user.
基本上,我想为每个用户存储一个特定的对象。
设置Firebase (Setting up Firebase)
I decided to use Firebase for this, and in particular the Firestore database they provide.
我决定为此使用Firebase ,尤其是他们提供的Firestore数据库 。
The free tier for it is generous, with up to 1GB of data stored and 10GB of network transfer per month. Way exceeding my estimates for what I need!
它的免费层非常宽敞,每月最多可存储1GB数据和10GB网络传输。 远远超出了我对我所需要的估计!
Open the Firebase website at https://firebase.google.com/
打开Firebase网站, 网址为https://firebase.google.com/
Firebase is a Google product, so once you are logged in to Google, you are essentially also logged in to Firebase.
Firebase是Google的产品,因此,一旦您登录到Google,您实际上也将登录到Firebase。
I created a new Firebase project by clicking “Create a project”
我通过点击“创建项目”创建了一个新的Firebase项目
I gave it a name:
我给它起了个名字:
And that was it:
就是这样:
I clicked the “Web” icon next to iOS and Android, and I entered the app name:
我单击了iOS和Android旁边的“ Web”图标,然后输入了应用名称:
And Firebase immediately gave me the access keys I needed, along with some sample code:
Firebase立即给了我所需的访问密钥,以及一些示例代码:
Right after that, Firebase prompted me to add some security rules for the database.
之后,Firebase提示我为数据库添加一些安全规则。
You can choose 2 things by default: open to everyone, or closed to everyone. I started open to everyone, something they call test mode.
默认情况下,您可以选择两件事:向所有人开放或对所有人封闭。 我开始向所有人开放,他们称之为测试模式 。
That’s it! I was ready to go, by creating a collection.
而已! 我已经准备好了,创建了一个收藏夹。
What’s a collection? In the Firestore terminology, we can create many different collections and assign documents to each collection.
什么是收藏? 用Firestore术语,我们可以创建许多不同的集合,并为每个集合分配文档。
A document can then contain fields, and other collections.
然后,文档可以包含字段和其他集合。
It’s not much different than other NoSQL databases, like MongoDB.
它与其他NoSQL数据库(如MongoDB)没有太大区别。
I highly recommend watching the YouTube playlist on the subject, it’s very well done.
我强烈建议您观看有关该主题的YouTube播放列表 ,它做得很好。
So I added my collection, which I called users
.
因此,我添加了我的收藏集,称为users
。
I wanted to identify each user using a special string, which I call id
.
我想使用特殊的字符串(称为id
来标识每个用户。
前端代码 (The frontend code)
We’re now getting to the JavaScript part.
现在,我们进入JavaScript部分。
In the footer, I included those 2 files, provided by Firebase:
在页脚中,我包括了Firebase提供的这两个文件:
<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-firestore.js"></script>
then I added a DOMContentLoaded event listener, to make sure I ran the code when the DOM was ready:
然后添加了DOMContentLoaded事件侦听器 ,以确保在DOM准备就绪时运行了代码:
<script>
document.addEventListener('DOMContentLoaded', event => {
})
</script>
In there, I added the Firebase configuration:
在其中,我添加了Firebase配置:
const firebaseConfig = {
apiKey: "MY-API-KEY",
authDomain: "MY-AUTH-DOMAIN",
projectId: "MY-PROJECT-ID"
}
I passed this object to firebase.initializeApp()
, and then I called firebase.firestore()
to get the reference to the database object:
我将此对象传递给firebase.initializeApp()
,然后调用firebase.firestore()
以获得对数据库对象的引用:
firebase.initializeApp(firebaseConfig)
const db = firebase.firestore()
Now, I created a script to populate the user IDs from a list I had in my backend, using a simple loop:
现在,我创建了一个脚本,使用一个简单的循环从后端拥有的列表中填充用户标识:
const list = [/*...my list...*/]
list.forEach(item => {
db.collection('users').doc(item).set({})
})
..and I ran it once, to populate the database. I basically programmatically created a document for each user.
..然后我运行了一次,以填充数据库。 我基本上以编程方式为每个用户创建了一个文档 。
This is very important, because once I created a document, it meant I could restrict the permissions to only update those documents, and disallow adding new ones or deleting them (something we’ll do later)
这非常重要,因为一旦创建了文档,就意味着我可以限制仅更新那些文档的权限,而不允许添加新文档或删除它们(稍后我们将做)
Ok so now I had some complex logic to identify the user ID and the course ID, which I won’t go into because it’s not related to our task here.
好的,现在我有了一些复杂的逻辑来标识用户ID和课程ID,我将不再赘述,因为它与此处的任务无关。
Once I gathered that, I could get a reference to the object:
一旦收集到该信息,就可以得到对该对象的引用:
const id = /* the user ID */
const course = /* the course ID */
const docRef = db.doc(`membership/${id}`)
Great! Now I could get the document reference from Firebase:
大! 现在我可以从Firebase获取文档参考:
docRef.get().then(function(doc) {
if (doc.exists) {
const data = doc.data()
document.querySelector('button')
.addEventListener('click', () => {
data[course] = true
docRef.update(data)
})
} else {
//user does not exist..
}
})
My logic was actually much more complicated because I have other moving parts, but you get the idea!
我的逻辑实际上要复杂得多,因为我还有其他活动部分,但是您明白了!
I initialize the document data by calling doc.data()
and when the button is clicked, which I assume it’s the button to say “I completed the course”, we associated the true
boolean value to the club identifier.
我通过调用doc.data()
初始化文档数据,然后单击按钮(假设它是说“我完成了课程”的按钮)时,我们将true
布尔值与俱乐部标识符相关联。
Later on, on subsequent loadings of the courses list page, I can initialize the page and assign a class if the course was completed, like this:
稍后,在随后加载课程列表页面时,如果课程完成,我可以初始化页面并分配课程,如下所示:
for (const [key, value] of Object.entries(data[course])) {
const element = document.querySelector('.course-' + course)
if (element) {
element.classList.add('completed')
}
}
权限问题 (The permissions problem)
I started Firebase in test mode, remember? Which makes the database open to everyone - everyone with the access keys, which are public and published in the code shipped to the frontend.
我以测试模式启动Firebase,记得吗? 这使得数据库对所有人开放-每个人都具有访问密钥,这些访问密钥是公开的,并在发布到前端的代码中发布。
So I had to do one thing: decide the level of permission allowed.
因此,我必须做一件事:确定允许的权限级别。
And I stumbled upon a pretty important problem.
我偶然发现了一个非常重要的问题。
Using the Firebase Console, under Rules, we can trim the permission. Initially this was the default rule:
使用Firebase控制台,在Rules(规则)下,可以调整权限。 最初,这是默认规则:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
I changed the rules to read, update
, so one can only update a document, not create new ones:
我将规则更改为read, update
,所以一个人只能更新一个文档,而不能创建新的文档:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, update;
}
}
}
But I was not able to prevent people to use the Firebase API, now freely available in the browser, to play around and list all the other documents in the collection - getting access to other people’s files.
但是我无法阻止人们使用Firebase API(现在可以在浏览器中免费使用)来浏览并列出集合中的所有其他文档,从而可以访问其他人的文件。
While this didn’t handle any sensitive data, it was not possible to ship this code.
尽管这不能处理任何敏感数据,但无法交付此代码。
通过自定义API将代码从前端移动到后端 (Moving the code from frontend to the backend via a custom API)
The permission issue was a road blocker.
许可问题是路障。
I thought about removing all the code I had, but eventually I figured out that I could hide all the API access from the browser completely and use a Node.js service to run the Firebase API.
我考虑过删除所有代码,但最终我发现可以完全隐藏浏览器中的所有API访问,并使用Node.js服务运行Firebase API。
This is also a common method to hide private/secret keys required by services: hide them behind a server you control.
这也是隐藏服务所需私钥/私钥的常用方法:将它们隐藏在您控制的服务器后面。
Instead of calling the Firebase from the browser, I created a set of endpoints on my own server, for example:
我没有从浏览器调用Firebase,而是在自己的服务器上创建了一组端点,例如:
POST to
/course
to set a course as completedPOST到
/course
将/course
设置为已完成POST to
/data
to get the data associated to the userPOST到
/data
以获取与用户关联的数据
and I access them using the Fetch API:
我使用Fetch API访问它们:
const options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ id, course, lesson })
}
const url = BASE_URL + '/lesson'
fetch(url, options).catch(err => {
console.error('Request failed', err)
})
All the logic with button clicks and so on remains in the client-side code, of course, I just moved the Firebase logic away.
所有带有单击按钮等等的逻辑都保留在客户端代码中,当然,我只是将Firebase逻辑移开了。
On the Node.js server side, I installed the official firebase
package using npm install firebase
and required it:
在Node.js服务器端,我使用npm install firebase
安装了正式的firebase
软件包,并需要它:
const firebase = require('firebase')
I set up an Express server to use CORS and I initialized Firebase:
我将Express服务器设置为使用CORS,并初始化了Firebase:
const firebaseConfig = {
apiKey: process.env.APIKEY,
authDomain: process.env.AUTHDOMAIN,
projectId: process.env.PROJECTID
}
firebase.initializeApp(firebaseConfig)
const db = firebase.firestore()
Then the code is exactly like the one I used in the frontend, except it now fires on HTTP endpoint calls. This is the code that returns a specific document from our collection
然后该代码与我在前端中使用的代码完全相同,除了它现在可以在HTTP端点调用上触发。 这是从我们的收藏集中返回特定文档的代码
const getData = async (id) => {
const doc = await db.doc(`membership/${id}`).get()
const data = doc.data()
if (!data) {
console.error('member does not exist')
return
}
return data
}
app.post('/data', cors(), async (req, res) => {
const id = req.body.id
if (id) {
res.json(await getData(id))
return
}
res.end()
})
and here’s the API to set a course as completed:
这是将课程设置为已完成的API:
const setCourseAsCompleted = async (id, course) => {
const doc = await db.doc(`membership/${id}`).get()
const data = doc.data()
if (!data) {
console.error('member does not exist')
return
}
if (!data[course]) {
data[course] = {}
}
data[course]['done'] = true
db.doc(`membership/${id}`).update(data)
}
app.post('/course', cors(), (req, res) => {
const id = req.body.id
const course = req.body.course
if (id && course) {
setCourseAsCompleted(id, course)
res.end('ok')
return
}
res.end()
})
That’s basically it. There’s some more code required, to handle other logic, but the gist of Firebase is this one I posted. Now I am also able to add a user for my server-side service, and limit all other access to the Firebase API and strengthen the security on it.
基本上就是这样。 还需要更多代码来处理其他逻辑,但是我发布的是Firebase的要旨。 现在,我还可以为我的服务器端服务添加一个用户,并限制对Firebase API的所有其他访问并增强其安全性。
firebase创建数据库