Firebase Ebook PDF
Firebase Ebook PDF
Firebase Keys
I hope you will enjoy this book, please send me your feedback at
[email protected].
In this book we will go over some reasons on why the latest Firebase
might be just as impactful in web development as Angular itself, and
why the two combined could be the best thing that happened to web
development in a long time.
And then you start building your backend in your usual preferred
technology, under the form of a REST API that gets data in and out data
out of Postgresql, MySql or your favorite SQL Database, or even Mongo.
You probably will use one of the following:
If you are in the C# / .Net world you might go with the .NET
framework and the Entity Framework, etc.
You would probably go for your Go-To solution that you have been using
for years and build a custom REST API as you are used to, and that
would work.
But I'm going to ask you before doing that to consider an alternative that
could boost a lot your productivity as a Web Developer.
And if you really still want to use REST, we still have some very good
news for you!
This usually does not apply to the whole application: it's common that
an app has a series of screens that are simply CRUD screens, but there
is maybe another part of the app that is super custom and looks like an
inbox system for exchanging messages like Gmail.
We are many times building a fully custom backend when there are large
parts of it that could be up and running out of the box. But because a
part of it needs to be custom, we end up building the whole thing
custom.
Because it creates the notion that your whole backend can work out of
the box, which we know that most of the times is not true, our chat
application cannot be fully built without any backend and we know it.
And based on this we might end up not choosing a BaaS solution and
going for a fully custom solution where we handcraft a lot of REST
controllers one by one to make some very common modification
operations, which can be very time consuming, error-prone and
repetitive.
We might build 90% of our system using an out of the box solution, and
focus our development time, energy and brain cycles on the custom 10%
which is probably the heart of our app.
And that's what Firebase allows us to do, plus it makes building the custom
part of the Backend much simpler, as we will see
1 {
2 "url": "angular2-hello-world-write-first-application",
3 "description": "Angular 2 Tutorial For Beginners - Build Your First App - Hello World Ste
4 "duration": "2:49",
5 "tags": "BEGINNER",
6 videoUrl: "https://www.youtube.com/embed/du6sKwEFrhQ",
7 "longDescription": "This is step by step guide to create your first Angular 2 application
8 }
If we want to modify the lesson title, why can't we modify the object and
simply call db.save(modifiedObject) , or something very close to it?
get it in our backend and maybe map into a C#, Java etc object
using a mapping framework
There is maybe the perception that the Firebase Database it's something
to be used to build chat-rooms or apps with real-time needs like games.
It's actually a very general purpose database (it's a fork of MongoDB).
Unlike what you might think, most of your knowledge of SQL databases
and how to model data will still mostly apply while using the Firebase
database, we are going to cover some data modeling in a moment.
We can think of the Firebase database as a giant JSON object in the sky,
the thing is just one big object!
But take a look at the first level of the object: You find nodes named
Courses, Lessons. Those suspiciously sound like the names of SQL
Tables, don't they?
The goal here is to pass the idea that Data Modeling in Firebase is a lot
more similar to the SQL world than we might think!
In our example we are going to have as the Model:
some courses
some lessons
1 {
2 "url": "angular2-hello-world-write-first-application",
3 "description": "Angular 2 Tutorial For Beginners - Build Your First App - Hello World St
4 "duration": "2:49",
5 "tags": "BEGINNER",
6 videoUrl: "https://www.youtube.com/embed/du6sKwEFrhQ",
7 "longDescription": "This is step by step guide to create your first Angular 2 applicatio
8 lessons: [
9 {
10 "url": "angular2-hello-world-write-first-application",
11 "description": "Angular 2 Tutorial For Beginners - Build Your First App - Hello
12 "duration": "2:49",
13 "tags": "BEGINNER",
14 videoUrl: "https://www.youtube.com/embed/du6sKwEFrhQ",
18 "url": "angular2-build-your-first-component",
19 "description": "Building Your First Angular 2 Component - Component Composition
20 "duration": "2:07",
21 "tags": "BEGINNER",
22 videoUrl: "https://www.youtube.com/embed/VES1eTNxi1s",
23 "longDescription": "In this lesson we are going to see how to include a componen
24 },
25 ...
26 ]
27 }
This might seem like a sensible thing to do, but we probably just created a
performance problem.
Then we might as well nest the lessons inside the course, because we
are going to need them all the time. But in most cases that is not the
case. In most cases, we want to have the data for the courses without
having the data for the lessons.
In some cases, this might be what we need. But imagine a course with
500 lessons, and you only want to show its description and duration in a
list of courses. Real Time or not, modeling the data like we did above
would likely bring the database to a halt with only a small number of
users.
Let's remove the lessons completely and store them in a separate top-
level node, which is the closest we can get in Firebase to a relational
table:
We will get to those keys in a second, and they do have some awesome
properties! We can now see that the lessons node is a list and contains
a list of IDs, which are the lesson unique identifiers. By expanding the ID
property we get the lesson data plus a courseId .
But in this case we really only want to create a link between course and
lesson, so we don't need the value. So by convention, we are going to set
the value to "true".
This is just like the case of SQL databases: sometimes the fully
normalized data model does not apply and we need to denormalize.
When in doubt doing data modeling in Firebase, we suggest to error on
the side of flatness. At the same time, in Firebase denormalization is
normal: do not hesitate to create multiple views of the same data that
are specific to multiple screens, and keep them in sync with multi-path
updates.
Firebase Keys
I really think you should embrace the Firebase keys from the beginning,
it's best to not try to go around them as they will save you a world of
pain:
This means that if we use these keys as the keys of your lists, the lists
will be naturally ordered by timestamp up to the millisecond precision,
and after that inside each millisecond the keys will be in random order,
not necessarily in the order in which they were generated.
We are going to see these keys in action in the upcoming sections, right
now you are probably wondering the following:
How do I query the data, how do I modify it What about Joins, how do I join
the data back?
We are going to answer these questions, but before that one thing that
its better to know is that right now you cannot do SQL like queries
directly in Firebase.
If you want powerful search its better for the moment to set up a
dedicated search solution like its explained here, the Firebase API is
constantly expanding and might include more search options in the
future. But nothing replaces this type of Google-like full-text search
capability.
Still, the Firebase SDK does give us some very powerful querying
capabilities, without the need to learn a separate query language.
2
3 export const firebaseConfig = {
4 apiKey: "AIzaSyA0BcUcu4V8aHT_gM-32BhRcmqji4z-xts",
5 authDomain: "some-app.firebaseapp.com",
6 databaseURL: "https://some-app.firebaseio.com",
7 storageBucket: "some-app.appspot.com",
8 messagingSenderId: "290354329699"
9 };
10
11 initializeApp(firebaseConfig);
12
13 database().ref().on('value', snapshot => console.log(snapshot.val()));
14
15
This prints the whole database to the console, you do not want to do
this! You usually want to query an inner node of the database, like a
course or a lesson.
The data read from the Firebase SDK is cached, there is a cache layer
that will ensure that if you repeat the same query the data is retrieved
from the client side cache.
What we are going to do now is understand what happened with the new
value being received from the database, how does this work. Did we use
a long polling Ajax request?
Note that this is all transparent and internal to the Firebase SDK, we will
never have to code Websockets directly in order to use Firebase.
Also with a websocket if you want to send an object over the wire, you
only pay the payload cost of the object, and not all the standard HTTP
headers that the browser automatically includes in a normal Ajax
request. Those headers can actually be many times larger than the data
itself (for small payloads).
If you just want to send a counter over the wire, you might be
transferring a payload hundreds of times larger than what you would
expect due to these extra headers.
Firebase by itself does not have support for joining queries on the server,
you need to query each path you need by doing several queries:
get the ids of the lessons for the course from lessonsPerCourse
get the lessons for the course going into lessons based on
each lesson ID
If this proves an issue at scale, you can always create a "view" for the
data, where you create a node where you have the lessons data for a
given course. Chances are, this will never happen if you are paging the
data.
a client-side cache
a server-side push enabled websockets based transport layer
And that is where the AngularFire Library comes in, notice that if you
include AngularFire in your app you also get the Firebase SDK as well
and you can use it directly, more on that in a moment.
1 @NgModule({
2 declarations: [
3 AppComponent,
4 ...
5 ],
6 imports: [
7 BrowserModule,
8 AngularFireModule.initializeApp(firebaseConfig),
9 AngularFireDatabaseModule,
10 AngularFireAuthModule,
11 RouterModule.forRoot(routerConfig)
12 ],
13 providers: [...],
14 bootstrap: [AppComponent]
15 })
2
3 @Injectable()
5
6 constructor( @Inject(FirebaseApp) fb) {
7
8 const rootDbRef = fb.database().ref();
9
10 rootDebRef.on('value', snapshot => console.log(snapshot.val()));
11
12 ...
13 }
14
15 }
16
This is the equivalent of the example we have seen before, it reads the
whole database, don't do this! But it's a good Hello World do to if you are
just getting started.
Let's say that instead of reading the whole database you want to read
the list of courses, or a given lesson. The code would look like this:
4
5 rootDebRef.child('courses').on('value',
7
8 rootDebRef.child('lessons/-KT_udWS6pEmpLVrxlVw:').on('value',
10
11 }
12
But notice that here we are still using a callback interface, and not an
Observables-based API. This is because we are here in fact using the
API of the Firebase SDK directly.
Let's now see the AngularFire Observable API in action, and see how it
really is a perfect client-side complement for the Realtime Database.
5
6
7 @Injectable()
9
10 constructor(private db:AngularFireDatabase) {
11 }
12
13 findAllLessons():Observable<Lesson[]> {
14 return this.db.list('lessons')
15 .do(console.log)
16 .map(Lesson.fromJsonList);
17 }
18
19 }
20
21
the rst value emitted will be the current value of the list
The Observable remains "running" (it does not complete) so we get new
values over time, which is actually the most natural behavior of an
Observable.
1 findLessonById(lessonId:string):Observable<Lesson> {
2 return this.db.object(`lessons/${lessonId}`)
3 .map(Lesson.fromJson);
4 }
5
This query would return us an Observable whose values over time are
the different versions of a lesson with a given Id.
The two main AngularFire APIs are super convenient for handling parts
of the Real-Time Database as Observables. But we also can modify data
with AngularFire.
We could write a new course to the end of this list in the following way:
4 console.error
5 );
Notice that the call to push does not return an Observable, we get back a
promise-like object on which we can call then() .
But don't think that you have to use the Firebase SDK to be able to use the
Realtime database at all!
This is true, if you want you don't even need the SDK, let's go over why.
It turns out that everything in Firebase has an URL, starting at the root
URL. And that URL can be used to perform data retrieval and
modification operations:
an HTTP GET will read all the data under a given node
It's really that simple and you didn't have to write a single custom REST
endpoint! There is one catch though, you need to add .json at the end
of the URL.
For example, let's say that the root URL of your database is:
https://final-project-recording.firebaseio.com
And you want to read all courses under the first-level node courses . If
you do a GET against the following Url, you would get back JSON
containing all the courses:
https://final-project-
recording.firebaseio.com/courses.json
1 {
2 "-KT_udWMM0vKlnp-naE3": {
3 "courseListIcon": "https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-s
5 "iconUrl": "https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners
7 "url": "getting-started-with-angular2"
8 },
9 "-KT_udWj0knDpxHaxcKv": {
10 "courseListIcon": "https://angular-academy.s3.amazonaws.com/course-logos/observables_r
12 "iconUrl": "https://angular-academy.s3.amazonaws.com/thumbnails/services-and-http.jpg
15 }
16 }
So you get all this out of the box REST functionality without writing a
single line of code!
But you will probably prefer to use the SDK: it has caching built-in and
you can do atomic multi-path updates, unlike using the REST API.
But if you want to simply do CRUD, it works perfectly and its very
convenient.
Section - Firebase Authentication
Firebase is much more than just the Real Time Database. One of its
numerous features is the ability to authenticate users out of the box, via
either email and password or via external providers like Github.
How many Users and Roles SQL Database tables have you seen in the
projects that you worked on? It's the kind of thing that can be instantly
working out of the box from day one if we use a BaaS solution, but
instead we often rewrite it from scratch in each project.
3
4 initializeApp(firebaseConfig);
5
6 auth()
7 .signInWithEmailAndPassword('[email protected]', 'test123')
8 .then(onLoginSuccess)
9 .catch(onLoginError);
10
11 function onLoginSuccess() {
12 ...
13 }
14
15 function onLoginError() {
16 ...
17 }
18
Authentication is only one of the many problems that Firebase can solve
for us out of the box. Other functionality that we need to implement an
application includes Cloud Messaging, Hosting, Storage, a Test Lab,
Crash Reporting and much more.
And that's it, 3 static files! One of the advantages of single page apps
that does not get mentioned a lot is how easy it is to deploy them in
production: it's just a few static files, upload them to Amazon S3 or your
nginx / apache server and it's done! At least the frontend part.
What better place to upload those files than to the Firebase servers
themselves, so that you don't have to do a separate DNS lookup to get
those files from somewhere else? There is an SSL certificate provisioned
so it's secure out of the box.
Not to mention that you get full HTTP 2 support if using Firebase
Hosting for that. You also want to upload all your images to Firebase
Hosting as well. You want to benefit from that blazing performance of
having only one TCP/IP connection to get your app index.html , CSS
and Javascript bundles as well as your images, all in one single DNS
request.
And if you don't have HTTP 2 support, Firebase Hosting is still a pretty
convenient solution just like the rest of Firebase as its really simple to
upload files via the command line, have a look at this Firecast on
Hosting to see it in action.
I hope this helps get the most out of Firebase and AngularFire! If you
have any questions about the book, any issues or comments I would
love to hear from you at [email protected]
I invite you to have a look at the bonus material below, I hope that you
enjoyed this book and I will talk to you soon.
Kind Regards,
Vasco
Angular University