Web app development isn't hard, but there sure is a lot of material to go over. I wanted to create a comprehensive course because I feel if someone is serious on building something great, you need to have knowledge on many of the different aspects of creating web apps. In addition, I didn't want to create a boring course which goes over the basics of programming and then, separately, shows how to develop feature after feature without a cohesive vision.
Instead, this course is project based, meaning that we will develop an entire web application throughout the course, and along the way, learn about programming basics and all of the different aspects on web app development as we encounter the need to implement certain features or learn certain concepts.
In this course our main project will be building a recipe search engine using Python and Google App Engine! Yeah, a full-blown recipe search engine. It's kind of like a smaller version of Google, where the only thing you can search for are recipes instead of the entire web. Check the "What am I going to get from this course?" to see all of the different concepts we are going to learn :)
Python is in my eyes, the best programming language to learn when you're starting. It reads like english so it's easy to understand the code and what it's doing, and Python tends to be terse, meaning that it's neatly concise and free of syntactical and verbal redundancy. Or in other words, it's clean and productive, where one line of Python code can do what maybe 5-6 lines of code do in other languages. Python is also a lot of fun to code!
Google App Engine is an astonishing technology we can use to deploy our apps. It takes away all of the headaches of managing servers and infrastructure so you can focus 100% on your application code. Whenever your app gets popular and receive a peak of traffic you don't have to worry about your servers crashing over the increased load. Google App Engine automagically spins up new servers to serve your users. In addition, App Engine provides many extremely useful services we will use, but that we don't have to install and configure. For example, we don't need to install nor configure a database, a email server, full text search indices, queues, etc. All of that is already build by Google on App Engine and we simply need to call those services to use them. Do you know how much time that saves us in the development process? A ton!
All of that allows us to rapidly develop apps, test them out and figure if we are getting traction or if we need to improve our product. It's this ability of fast development cycles that leads us to find our ideas that work, and hopefully, generate revenues and lead us to live the lifestyle we dream of.
The web runs on many interconnected computers and servers. Clients (users) send requests to access websites (hosted on servers). To do that, we type in the domain name. This domain name is resolved to the IP address of the server that hosts the given application or website. When our server receives our request, the code living on the server interprets the request, figures out what it needs to do, and sends back a response. The response in most cases contains HTML which our browser then picks up and renders that graphical representation on our screens.
Paths are going to be the most important part of understanding how to develop our web apps.
We, the users, send requests to web apps. The request contains a header, and sometimes a body (depending on the type of request). The server evaluates the request, and the code living on the server figures out what to do, and once it know what to give back to the user, it sends back a response. There is only 1 type of response, and it contains both the header and body.
Headers contain extra useful information to provide the desired functionality. It's information that we don't see in our browser, as it's not part of the body. Common examples of data sent in headers are user session information when the user is logged in. If the header entry responsible for passing the user session was not present, the server would not know if the user is logged in or not.
App architecture is a complex subject. Managing the architecture when your app gets popular is even more complex and requires hiring a team of trained DevOps people.
App Engine's architecture is much simpler, as it's abstracted into different services we can choose to use as part of our application stack, or not. If we choose to use a given service, all we need to do is call the service using the APIs provided in the Google Cloud SDK. There's no installing, configuring, monitoring or DevOps involved. Many head-aches are solved from the get-go :D
In this lecture we will learn how to install the Google Cloud SDK. The SDK allows us to have access to the same functionalities our app will have on Google's servers, on our local machine. This allows for easy development as the behaviour of our app will be the same on our local machine as on Google's servers. Awesome! :)
We're going to create and run our first project! With only two files and absolutely no configuration your are able to run an App Engine application on your local machine, that's really cool!
The app.yaml is kind of like a configuration file. The most important part of it is the "handlers" section where we map incoming requests to a router file. Then, we have the main.py file which contains the router and the request handler which handles incoming requests.
Brief intro into classes. Classes are a way to model information and structure together logical components and functions. In out web app, a class represents a incoming request, containing all the headers, and a response, containing the headers and body of said response. We can change the default headers being sent back in the response. We can also add new headers. We also have to provide the body of the response. All this is done with class methods already provided by the webapp2 framework. We will explore these concepts further along the course.
A response is the thing we are always going to be sending back to our clients. It contains the headers (which are set by default by the webapp2 framework), and the body generated by our code. The response is the thing that the client's browser grabs to interpret it, and render something on their screen.
Routes are the most important concept we need to take away from this section. A route is a way of telling App Engine which bit of code needs to process the incoming request. This is done by mapping the path of the incoming request, to the function (Request Handler) that we want to handle it.
HTML is the markup language that tells browsers how to render things on screen. We go over the very basics in this lecture, and will go into more detail in the Recipe Search Engine project further along.
Here we're actually developing the function, or Request Handler, that actually processes an incoming request sent to us by users to the path /greet
Unlike other services, we don't need to install and setup our database (DataStore in App Engine). This is already done for us. All we need to do is use the API (Application Programming Interface) which is provided in the Google Cloud SDK (Software Development Kit). The API is imported into our project by writing
from google.appengine.ext import ndb
Before we move along, we need to understand lists. Lista are a collection of items (any type of item, any amount of it) stored under the same variable name. We can do many things with lists, such as sort them, test whether some item is in the list or not, and access each item one by one.
In order to add our greetings, we need to break up our HTML a little to insert said greetings.
Fetching items from the DataStore does not require knowledge of a Query Language such as SQL. Instead, we perform queries directly with code. Isn't that much better? :)
To fetch all greetings (not filter the results, nor order them) we can simply perform the following line of code to save all greetings into the "greetings" variable.
greetings = Greetings.query().fetch()
Once we have the greetings (in a list), it's just a matter of looping or iterating over them and inserting them into our HTML
Working with huge strings, and breaking them up to insert our results from the datastore is very cumbersome. It's much better to use templates, which allow us to separate the HTML (front end) from our backend code. The backend will simply call the template in question and send it back to our clients as the response. Everything will be much tidier :D
We need to add the list of greetings into a key in our dictionary, which we will then pass into the Jinja template. This list of greetings will then be accessible from the template by the name if the key we give it
The key concept is that every file should have its specific purpose. It's generally not a good idea to write many different functionalities belonging to different logical groups in the same file.
This is why we are going to move out model (which is a logical element of treating with data in out datastore) out of our request handler (which is code dedicated to receive a users request, figure out what to do, and send back a response).
We're going to do the same thing with our router :)
We're going to build a recipe search engine and along the way learn about:
Whoa...! Let's get to work!
Make sure to have a Google account (gmail.com) and be logged in. Then go to: https://console.developers.google.com
Once there, go ahead an hit "Create new project"
The app.yaml is sort of like a configuration file which tells App Engine some project details such as the application version, the handlers, libraries we want to use, etc. We won't be working on this file too much, and I tend to not write it from scratch. I always copy it from project to project and adapt it. And for that reason, I've provided a app.yaml sample file you can download ;)
When you set out to build a larger project, we need to take care of the structure and make sure we can find items easily later on, so the project structure is pretty important
The router is what's going to map the incoming requests with request handlers. It's part of the webapp2 framework so we can simply import and use it.
We need to add functionality to the default, stock Request Handler that webapp2 provides. To do just that, we are going to create our own Request Handler class and extend off of the webapp2.RequestHandler.
Our new YumSearchRequestHandler will the the code that's used by all of our pages and thus any code we need to be implemented in all pages will be written in this class.
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
In order to not have duplicate code for every single template, we need to abstract all of the common parts out into a separate, base template.
We will then extend off of the "main.html", or the base template to all other templates so we don't have to write the base code all of the time. Also, when we make changes to the base HTML, we only need to do them in one place :)
The click listener detects when we click a button and performs some logic that we specify within the function. I n our case, we will be launching an asynchronous request to the server. Isn't that awesome? :)
Here we are actually sending the AJAX Post request to our server. AJAX is really neat because it allows us to call the server without refreshing the entire page. This brings many benefits such as:
It's always a great idea to give the user some feedback if our site is "working". We can do this by changing the appearance of the button. If the request is pending to be received, we can change the button to say "Registering..." and add a icon that lets the user know the site is working
This code will pick up the values sent to it by the AJAX form, and if all is correct, it will go ahead and register the user
We use ifs to test different cases. Inside the if we must always provide a test that evaluates to either True of False. For example:
if 5 > 10
Here the test is "5 > 10", and will evaluate to False
Since it's code that will be used by more Request Handlers, we can abstract it and put it into the common area, our YumSearchRequestHandler
Never, ever, ever store passwords as plain text. We need to encrypt them.
There seems to be a bug in the gcloud commands with email sending. We need to quit out of our app, and start it in another way:
First, I'm going to change directory to some folder which from there I can navigate forward both to my project and to the SDK folder. For me, that'll be the home directory (~)
Then we need to start the dev_appserver.py located in google-cloud-sdk/platform/google_appengine and pass in the directory to the app.yaml file in our app
python google-cloud-sdk/platform/google_appengine/dev_appserver.py Projects/AppEngine/yum-search-2/default
Full Text Search allows us to search through millions of indexed documents using a couple of keywords. The documents retrieved by the search will contain those keywords.
This is something hard to achieve with a traditional database such as MySQL or even the DataStore for that matter. Databases aren't really designed for that use case, that's why we need to use the Search API
My all-time favourite quote is "If everything seems under control, you're just not going fast enough" by Mario Andretti. I wake up each day dreaming of creating truly outstanding and engaging products millions of people will want to use.
I studied Business Management, but technology always laid deep in my heart from the moment I first got my hands on a computer. Since a young age, I self-taught myself coding and web development, both front-end as well as back-end.
I can firmly say that having both Business and Technological skills has allowed me to get the jobs I always wanted to get at amazing tech companies, but more importantly, has allowed me to be free. Freedom in the form of being capable of building projects and startups that allow me to live the life I always dreamt of.
My personal goal is to pass this freedom on to more people, hopefully you too.