One place for hosting & domains

      Build

      Build a RESTful API with Flask – The TDD Way: Part 2

      Introduction

      In Part 1 of this series, we learned how to create a RESTful API the TDD way. We covered writing tests and learned a lot about Flask. If you haven’t read Part 1, please do because this tutorial will build upon it.

      In this part of the series, we’ll learn how to authenticate and authorize users in our API.

      In this tutorial, we’ll talk about securing our API with token-based authentication and user authorization. We will integrate users into the API we built in Part 1.

      In order to get started, ensure your virtual environment is activated.

      We intend to allow bucketlists to be owned by users. For now, anyone can manipulate a bucketlist even if they did not create it. We’ve got to fix this security hole.

      How do we keep track of users, you ask? We define a model.

      
      
      from app import db
      from flask_bcrypt import Bcrypt
      
      class User(db.Model):
          """This class defines the users table """
      
          __tablename__ = 'users'
      
          
          id = db.Column(db.Integer, primary_key=True)
          email = db.Column(db.String(256), nullable=False, unique=True)
          password = db.Column(db.String(256), nullable=False)
          bucketlists = db.relationship(
              'Bucketlist', order_by='Bucketlist.id', cascade="all, delete-orphan")
      
          def __init__(self, email, password):
              """Initialize the user with an email and a password."""
              self.email = email
              self.password = Bcrypt().generate_password_hash(password).decode()
      
          def password_is_valid(self, password):
              """
              Checks the password against it's hash to validates the user's password
              """
              return Bcrypt().check_password_hash(self.password, password)
      
          def save(self):
              """Save a user to the database.
              This includes creating a new user and editing one.
              """
              db.session.add(self)
              db.session.commit()
      
      class Bucketlist(db.Model):
          """This class defines the bucketlist table."""
      
          __tablename__ = 'bucketlists'
      
          
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(255))
          date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
          date_modified = db.Column(
              db.DateTime, default=db.func.current_timestamp(),
              onupdate=db.func.current_timestamp())
          created_by = db.Column(db.Integer, db.ForeignKey(User.id))
      
          def __init__(self, name, created_by):
              """Initialize the bucketlist with a name and its creator."""
              self.name = name
              self.created_by = created_by
      
          def save(self):
              """Save a bucketlist.
              This applies for both creating a new bucketlist
              and updating an existing onupdate
              """
              db.session.add(self)
              db.session.commit()
      
          @staticmethod
          def get_all(user_id):
              """This method gets all the bucketlists for a given user."""
              return Bucketlist.query.filter_by(created_by=user_id)
      
          def delete(self):
              """Deletes a given bucketlist."""
              db.session.delete(self)
              db.session.commit()
      
          def __repr__(self):
              """Return a representation of a bucketlist instance."""
              return "<Bucketlist: {}>".format(self.name)
      

      Here’s what we’ve done:

      • We imported Flask-Bcrypt extension to help us in hashing our passwords. You should never store passwords in plaintext.
      • We created a User model that represents the users table. It contains the email and password fields to capture the user’s credentials.
      • Since a user can own many bucketlists, we defined a One-to-Many relationship between the two tables. We defined this relationship by adding the db.relationship() function on the User table (parent table)
      • We added a foreign key on the child table (Bucketlist) referencing the User table. The foreign key has some arguments. The cascade="all, delete-orphan" will delete all bucketlists when a referenced user is deleted.
      • We hash the password by using generate_password_hash(password). This will make our users’ passwords be secure from dictionary and brute force attacks.
      • We refactored the get_all() method to get all the bucketlists for a given user.

      Don’t forget to install Flask-Bcrypt

      1. pip install flask-bcrypt

      Migrate them

      Migrate the changes we’ve just made to the database we initially created in Part 1 of the series.

      1. python manage.py db migrate
      2. python manage.py db upgrade

      Now we have a user table to keep track of registered users.

      Our app will have many tests from now on. It’s best practice to have a test folder that will house all our tests. We’ll create a folder called tests. Inside this folder, we’ll move our test_bucketlists.py file into it.

      Our directory structure should now look like this:

      1. ├── bucketlist
      2. ├── app
      3. │ ├── __init__.py
      4. │ └── models.py
      5. ├── instance
      6. │ ├── __init__.py
      7. │ └── config.py
      8. ├── manage.py
      9. ├── requirements.txt
      10. ├── run.py
      11. ├── tests
      12. │ └── test_bucketlist.py

      Also, we’ll edit the manage.py as follows:

      import os
      import unittest
      
      from flask_script import Manager
      from flask_migrate import Migrate, MigrateCommand
      from app import db, create_app
      
      
      app = create_app(config_name=os.getenv('APP_SETTINGS'))
      migrate = Migrate(app, db)
      
      manager = Manager(app)
      
      
      
      manager.add_command('db', MigrateCommand)
      
      
      
      @manager.command
      def test():
          """Runs the unit tests without test coverage."""
          tests = unittest.TestLoader().discover('./tests', pattern='test*.py')
          result = unittest.TextTestRunner(verbosity=2).run(tests)
          if result.wasSuccessful():
              return 0
          return 1
      
      
      if __name__ == '__main__':
          manager.run()
      

      The decorator on top of test() allows us to define a command called test. Inside the function, we load the tests from the tests folder using the TestLoader() class and then run them with TextTestRunner.run(). If it’s successful, we exit gracefully with a return 0.

      Let’s test it out on our terminal.

      1. python manage.py test

      The tests should fail. This is because we’ve not modified our code to work with the new changes in the model.

      From now on, we’ll use this command to run our tests.

      Token-based authentication is a security technique that authenticates users who attempt to login to a server using a security token provided by the server. Without the token, a user won’t be granted access to restricted resources. You can find more intricate details about token-based authentication here

      For us to implement this authentication, we’ll use a Python package called PyJWT. PyJWT allows us to encode and decode JSON Web Tokens (JWT).

      That being said, let’s install it:

      1. pip install PyJWT

      For our users to authenticate, the access token is going to be placed in the Authorization HTTP header in all our bucketlist requests.

      Here’s how the header looks like:

      Authorization:  "Bearer <The-access-token-is-here>"
      

      We’ll put the word Bearer before the token and separate them with a space character.

      Don’t forget the space in between the Bearer and the token.

      Encode and Decode the Token

      We need to create a way to encode the token before it’s sent to the user. We also need to have a way to decode the token when the user sends it via the Authorization header.

      In our model.py we’ll create a function inside our User model to generate the token and another one to decode it. Let’s add the following code:

      
      
      
      import jwt
      from datetime import datetime, timedelta
      
      class User(db.Model):
          """Maps to users table """
      
          __tablename__ = 'users'
      
          
          
          
      
          def __init__(self, email, password):
              
              
      
          def password_is_valid(self, password):
              
              
      
          def save(self):
              
              
      
          def generate_token(self, user_id):
              """ Generates the access token"""
      
              try:
                  
                  payload = {
                      'exp': datetime.utcnow() + timedelta(minutes=5),
                      'iat': datetime.utcnow(),
                      'sub': user_id
                  }
                  
                  jwt_string = jwt.encode(
                      payload,
                      current_app.config.get('SECRET'),
                      algorithm='HS256'
                  )
                  return jwt_string
      
              except Exception as e:
                  
                  return str(e)
      
          @staticmethod
          def decode_token(token):
              """Decodes the access token from the Authorization header."""
              try:
                  
                  payload = jwt.decode(token, current_app.config.get('SECRET'))
                  return payload['sub']
              except jwt.ExpiredSignatureError:
                  
                  return "Expired token. Please login to get a new token"
              except jwt.InvalidTokenError:
                  
                  return "Invalid token. Please register or login"
      

      The generate_token() takes in a user ID as an argument, uses jwt to create a token using the secret key, and makes it time-based by defining its expiration time. The token is valid for 5 minutes as specified in the timedelta. You can set it to your liking.

      The decode_token() takes in a token as an argument and checks whether the token is valid. If it is, it returns the user ID as the payload. It returns an error message if the token is expired or invalid.

      Don’t forget to import jwt and the datetime above.

      Our app is growing bigger. We’ll have to organize it into components. Flask uses the concept of Blueprints to make application components.

      Blueprints are simply a set of operations that can be registered on a given app. Think of it as an extension of the app that can address a specific functionality.

      We’ll create an authentication blueprint.
      This blueprint will focus on handling user registration and logins.

      Inside our /app directory create a folder and call it auth.

      Our auth folder should contain:

      • __init__.py file
      • views.py file

      In our auth/__init__.py file, initialize a blueprint.

      
      
      from flask import Blueprint
      
      
      auth_blueprint = Blueprint('auth', __name__)
      
      from . import views
      

      Then import the blueprint and register it at the bottom of the app/__init__.py, just before the return app line.

      
      
      
      
      def create_app(config_name):
        
        
        
      
        @app.route('/bucketlists/<int:id>', methods=['GET', 'PUT', 'DELETE'])
        def bucketlist_manipulation(id, **kwargs):
               
          
          
          ...
      
        
        from .auth import auth_blueprint
        app.register_blueprint(auth_blueprint)
      
        return app
      

      Testing should never be an afterthought. It should always come first.

      We’re going to add a new test file that will house all our tests for the authentication blueprint.
      It’ll test whether our API can handle user registration, user log in, and access-token generation.

      In our tests directory, create a file naming it test_auth.py. Write the following code in it:

      
      
      import unittest
      import json
      from app import create_app, db
      
      class AuthTestCase(unittest.TestCase):
          """Test case for the authentication blueprint."""
      
          def setUp(self):
              """Set up test variables."""
              self.app = create_app(config_name="testing")
              
              self.client = self.app.test_client
              
              self.user_data = {
                  'email': '[email protected]',
                  'password': 'test_password'
              }
      
              with self.app.app_context():
                  
                  db.session.close()
                  db.drop_all()
                  db.create_all()
      
          def test_registration(self):
              """Test user registration works correcty."""
              res = self.client().post('/auth/register', data=self.user_data)
              
              result = json.loads(res.data.decode())
              
              self.assertEqual(result['message'], "You registered successfully.")
              self.assertEqual(res.status_code, 201)
      
          def test_already_registered_user(self):
              """Test that a user cannot be registered twice."""
              res = self.client().post('/auth/register', data=self.user_data)
              self.assertEqual(res.status_code, 201)
              second_res = self.client().post('/auth/register', data=self.user_data)
              self.assertEqual(second_res.status_code, 202)
              
              result = json.loads(second_res.data.decode())
              self.assertEqual(
                  result['message'], "User already exists. Please login.")
      

      We’ve initialized our test with a test client for making requests to our API and some test data.
      The first test function test_registration() sends a post request to /auth/register and tests the response it gets. It ensures that the status code is 201, meaning we’ve successfully created a user.
      The second test function tests whether the API can only register a user once. Having duplicates in the database is bad for business.

      Now let’s run the tests using python manage.py test. The tests should fail.

      1. ----------------------------------------------------------------------
      2. raise JSONDecodeError("Expecting value", s, err.value) from None
      3. json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

      The reason our tests fail is simply because we lack the functionality they need to test. Let’s implement something that’ll make these two tests pass.

      Open up the views.py file and add the following code:

      
      
      from . import auth_blueprint
      
      from flask.views import MethodView
      from flask import make_response, request, jsonify
      from app.models import User
      
      class RegistrationView(MethodView):
          """This class registers a new user."""
      
          def post(self):
              """Handle POST request for this view. Url ---> /auth/register"""
      
              
              user = User.query.filter_by(email=request.data['email']).first()
      
              if not user:
                  
                  try:
                      post_data = request.data
                      
                      email = post_data['email']
                      password = post_data['password']
                      user = User(email=email, password=password)
                      user.save()
      
                      response = {
                          'message': 'You registered successfully. Please log in.'
                      }
                      
                      return make_response(jsonify(response)), 201
                  except Exception as e:
                      
                      response = {
                          'message': str(e)
                      }
                      return make_response(jsonify(response)), 401
              else:
                  
                  
                  response = {
                      'message': 'User already exists. Please login.'
                  }
      
                  return make_response(jsonify(response)), 202
      
      registration_view = RegistrationView.as_view('register_view')
      
      
      auth_blueprint.add_url_rule(
          '/auth/register',
          view_func=registration_view,
          methods=['POST'])
      

      Here’s what we have added:

      • We imported our blueprint together with Flask’s make_response (for returning our response) and jsonify (for encoding our data in JSON and adding an application/json header to the response)
      • We’ve defined a class-based view to handle registration by dispatching a POST request to our post() method.
      • Inside our post() method, we check if the user exists in our database. If they don’t, we create a new user and return a message to them notifying their successful registration.
        If the user exists they are reminded to log in.
      • Lastly, we used as_view() method to make our class-based view callable so that it can take a request and return a response. We then defined the URL for registering a user as /auth/register.

      Let’s run our tests once more. Only the AuthTestCase tests should pass. The bucketlist tests still fail because we haven’t modified the __init__.py code.

      1. test_already_registered_user (test_auth.AuthTestCase)
      2. Test that a user cannot be registered twice. ... ok
      3. test_registration (test_auth.AuthTestCase)
      4. Test user registration works correcty. ... ok
      5. Bucketlist failed tests fall here
      6. ----------------------------------------------------------------------

      Using Postman for auth/register

      We’ll test our registration functionality by making a request using Postman.

      But before we make the requests, ensure the API is up and running.

      1. python run.py development
       * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
       * Restarting with stat
       * Debugger is active!
       * Debugger PIN: 225-021-817
      

      Now you can make a POST request to localhost:5000/auth/register. Specify an email and a password of your choice to represent the user we are registering. Click send.

      A user will have to log in to gain access to our API. Currently, we are lacking this login functionality. Let’s start with some tests. We’ll add two more tests at the bottom of our test_auth.py as follows:

      
      class AuthTestCase(unittest.TestCase):
          """Test case for the authentication blueprint."""
      
          def setUp(self):
              
      
          def test_registration(self):
              
      
          def test_already_registered_user(self):
              
      
          def test_user_login(self):
              """Test registered user can login."""
              res = self.client().post('/auth/register', data=self.user_data)
              self.assertEqual(res.status_code, 201)
              login_res = self.client().post('/auth/login', data=self.user_data)
      
              
              result = json.loads(login_res.data.decode())
              
              self.assertEqual(result['message'], "You logged in successfully.")
              
              self.assertEqual(login_res.status_code, 200)
              self.assertTrue(result['access_token'])
      
          def test_non_registered_user_login(self):
              """Test non registered users cannot login."""
              
              not_a_user = {
                  'email': '[email protected]',
                  'password': 'nope'
              }
              
              res = self.client().post('/auth/login', data=not_a_user)
              
              result = json.loads(res.data.decode())
      
              
              
              self.assertEqual(res.status_code, 401)
              self.assertEqual(
                  result['message'], "Invalid email or password, Please try again")
      

      The test_user_login() function tests whether our API can successfully log in as a registered user. It also tests for the access token.

      The other test function test_non_registered_user_login() tests whether our API can restrict signing in to only registered users.

      Again, we’ll make the tests pass by implementing its functionality. Let’s create the login view.

      from . import auth_blueprint
      
      from flask.views import MethodView
      from flask import Blueprint, make_response, request, jsonify
      from app.models import User
      
      class RegistrationView(MethodView):
          """This class-based view registers a new user."""
          
          
      
      class LoginView(MethodView):
          """This class-based view handles user login and access token generation."""
      
          def post(self):
              """Handle POST request for this view. Url ---> /auth/login"""
              try:
                  
                  user = User.query.filter_by(email=request.data['email']).first()
      
                  
                  if user and user.password_is_valid(request.data['password']):
                      
                      access_token = user.generate_token(user.id)
                      if access_token:
                          response = {
                              'message': 'You logged in successfully.',
                              'access_token': access_token.decode()
                          }
                          return make_response(jsonify(response)), 200
                  else:
                      
                      response = {
                          'message': 'Invalid email or password, Please try again'
                      }
                      return make_response(jsonify(response)), 401
      
              except Exception as e:
                  
                  response = {
                      'message': str(e)
                  }
                  
                  return make_response(jsonify(response)), 500
      
      
      registration_view = RegistrationView.as_view('registration_view')
      login_view = LoginView.as_view('login_view')
      
      
      
      auth_blueprint.add_url_rule(
          '/auth/register',
          view_func=registration_view,
          methods=['POST'])
      
      
      
      auth_blueprint.add_url_rule(
          '/auth/login',
          view_func=login_view,
          methods=['POST']
      )
      

      Here, we’ve defined a class-based view just like we did in the registration section.

      It dispatches the POST request to the post() method as well. This is to capture the user credentials (email, password) when they log in. It checks whether the password given is valid, generates an access token for the user, and returns a response containing the token.

      We’ve also handled exceptions gracefully so that if one occurs, our API will continue running and won’t crush.

      Finally, we defined a URL for the login route.

      Logging in on Postman with auth/login

      Make a POST request. Input the email and password we specified for the user during registration. Click send. You should get an access token in the JSON response.

      Running the tests

      If you run the tests, you will notice that the login tests pass, but the bucketlist one still fails. It’s time to refactor these tests.

      First, we’ll create two helper functions for registering and signing in to our test user.

      
      class BucketlistTestCase(unittest.TestCase):
          """This class represents the bucketlist test case"""
      
          def setUp(self):
              """Set up test variables."""
         
         
      
          def register_user(self, email="[email protected]", password="test1234"):
              """This helper method helps register a test user."""
              user_data = {
                  'email': email,
                  'password': password
              }
              return self.client().post('/auth/register', data=user_data)
      
          def login_user(self, email="[email protected]", password="test1234"):
              """This helper method helps log in a test user."""
              user_data = {
                  'email': email,
                  'password': password
              }
              return self.client().post('/auth/login', data=user_data)
      
          
          
      
      
      if __name__ == "__main__":
          unittest.main()
      

      We do this so that when we want to register or log in as a test user (which is in all the tests), we don’t have to repeat ourselves. We’ll simply call the function and we are set.

      Next, we’ll define a way to get the access token and add it to the Authorization header in all our client requests. Here’s a code snippet of how we’re going to do it.

          def test_bucketlist_creation(self):
              """Test the API can create a bucketlist (POST request)"""
              
              self.register_user():
              result = self.login_user()
              
              access_token = json.loads(result.data.decode())['access_token']
      
              
              res = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data=self.bucketlist)
      

      We can now go ahead and refactor the whole test_bucketlist.py file. After refactoring all our requests, we should have something like this:

      import unittest
      import os
      import json
      from app import create_app, db
      
      class BucketlistTestCase(unittest.TestCase):
          """This class represents the bucketlist test case"""
      
          def setUp(self):
              """Define test variables and initialize app."""
              self.app = create_app(config_name="testing")
              self.client = self.app.test_client
              self.bucketlist = {'name': 'Go to Borabora for vacay'}
      
              
              with self.app.app_context():
                  
                  db.session.close()
                  db.drop_all()
                  db.create_all()
      
          def register_user(self, email="[email protected]", password="test1234"):
              user_data = {
                  'email': email,
                  'password': password
              }
              return self.client().post('/auth/register', data=user_data)
      
          def login_user(self, email="[email protected]", password="test1234"):
              user_data = {
                  'email': email,
                  'password': password
              }
              return self.client().post('/auth/login', data=user_data)
      
          def test_bucketlist_creation(self):
              """Test API can create a bucketlist (POST request)"""
              self.register_user()
              result = self.login_user()
              access_token = json.loads(result.data.decode())['access_token']
      
              
              res = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data=self.bucketlist)
              self.assertEqual(res.status_code, 201)
              self.assertIn('Go to Borabora', str(res.data))
      
          def test_api_can_get_all_bucketlists(self):
              """Test API can get a bucketlist (GET request)."""
              self.register_user()
              result = self.login_user()
              access_token = json.loads(result.data.decode())['access_token']
      
              
              res = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data=self.bucketlist)
              self.assertEqual(res.status_code, 201)
      
              
              res = self.client().get(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
              )
              self.assertEqual(res.status_code, 200)
              self.assertIn('Go to Borabora', str(res.data))
      
          def test_api_can_get_bucketlist_by_id(self):
              """Test API can get a single bucketlist by using it's id."""
              self.register_user()
              result = self.login_user()
              access_token = json.loads(result.data.decode())['access_token']
      
              rv = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data=self.bucketlist)
      
              
              self.assertEqual(rv.status_code, 201)
              
              results = json.loads(rv.data.decode())
      
              result = self.client().get(
                  '/bucketlists/{}'.format(results['id']),
                  headers=dict(Authorization="Bearer " + access_token))
              
              self.assertEqual(result.status_code, 200)
              self.assertIn('Go to Borabora', str(result.data))
      
          def test_bucketlist_can_be_edited(self):
              """Test API can edit an existing bucketlist. (PUT request)"""
              self.register_user()
              result = self.login_user()
              access_token = json.loads(result.data.decode())['access_token']
      
              
              rv = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data={'name': 'Eat, pray and love'})
              self.assertEqual(rv.status_code, 201)
              
              results = json.loads(rv.data.decode())
      
              
              rv = self.client().put(
                  '/bucketlists/{}'.format(results['id']),
                  headers=dict(Authorization="Bearer " + access_token),
                  data={
                      "name": "Dont just eat, but also pray and love :-)"
                  })
              self.assertEqual(rv.status_code, 200)
      
              
              results = self.client().get(
                  '/bucketlists/{}'.format(results['id']),
                  headers=dict(Authorization="Bearer " + access_token))
              self.assertIn('Dont just eat', str(results.data))
      
          def test_bucketlist_deletion(self):
              """Test API can delete an existing bucketlist. (DELETE request)."""
              self.register_user()
              result = self.login_user()
              access_token = json.loads(result.data.decode())['access_token']
      
              rv = self.client().post(
                  '/bucketlists/',
                  headers=dict(Authorization="Bearer " + access_token),
                  data={'name': 'Eat, pray and love'})
              self.assertEqual(rv.status_code, 201)
              
              results = json.loads(rv.data.decode())
      
              
              res = self.client().delete(
                  '/bucketlists/{}'.format(results['id']),
                  headers=dict(Authorization="Bearer " + access_token),)
              self.assertEqual(res.status_code, 200)
      
              
              result = self.client().get(
                  '/bucketlists/1',
                  headers=dict(Authorization="Bearer " + access_token))
              self.assertEqual(result.status_code, 404)
      
      
      if __name__ == "__main__":
          unittest.main()
      

      We’ll refactor the methods that handle the HTTP requests for bucketlist creation and getting all the bucketlists. Open up /app/__init__.py file and edit as follows:

      
      
      
      from flask import request, jsonify, abort, make_response
      
      def create_app(config_name):
          from models import Bucketlist, User
      
          
          
          
      
          @app.route('/bucketlists/', methods=['POST', 'GET'])
          def bucketlists():
              
              auth_header = request.headers.get('Authorization')
              access_token = auth_header.split(" ")[1]
      
              if access_token:
             
                  user_id = User.decode_token(access_token)
                  if not isinstance(user_id, str):
                      
      
                      if request.method == "POST":
                          name = str(request.data.get('name', ''))
                          if name:
                              bucketlist = Bucketlist(name=name, created_by=user_id)
                              bucketlist.save()
                              response = jsonify({
                                  'id': bucketlist.id,
                                  'name': bucketlist.name,
                                  'date_created': bucketlist.date_created,
                                  'date_modified': bucketlist.date_modified,
                                  'created_by': user_id
                              })
      
                              return make_response(response), 201
      
                      else:
                          
                          bucketlists = Bucketlist.query.filter_by(created_by=user_id)
                          results = []
      
                          for bucketlist in bucketlists:
                              obj = {
                                  'id': bucketlist.id,
                                  'name': bucketlist.name,
                                  'date_created': bucketlist.date_created,
                                  'date_modified': bucketlist.date_modified,
                                  'created_by': bucketlist.created_by
                              }
                              results.append(obj)
      
                          return make_response(jsonify(results)), 200
                  else:
                      
                      message = user_id
                      response = {
                          'message': message
                      }
                      return make_response(jsonify(response)), 401
      

      We first added two imports: the User model and the make_response from Flask.

      In the bucketlist function, we check for the authorization header from the request and extract the access token. Then, we decoded the token using User.decode_token(token) to give us the payload. The payload is expected to be a user ID if the token is valid and not expired. If the token is not valid or expired, the payload will be an error message as a string.

      Create a bucketlist or two

      Copy the token and paste it to the header section, creating an Authorization header. Don’t forget to put the word Bearer before the token with a space separating them like this:

      Authorization:   "Bearer dfg32r22349r40eiwoijr232394029wfopi23r2.2342..."
      

      Make a POST request to localhost:5000/bucketlists/, specifying the name of the bucketlist. Click send.

      Get all bucketlists for a given user

      Ensure you’ve set the Authorization header just as we did for the POST request.

      Make a GET request to localhost:5000/bucketlists/ and retrieve all the bucketlists our user just created.

      We’ll refactor the PUT and DELETE functionality the same way we tackled the GET and POST.

      
      
      
      
      from flask import request, jsonify, abort, make_response
      
      def create_app(config_name):
          from models import Bucketlist, User
      
          
          
          
      
          @app.route('/bucketlists/', methods=['POST', 'GET'])
          def bucketlists():
              
              
      
          @app.route('/bucketlists/<int:id>', methods=['GET', 'PUT', 'DELETE'])
          def bucketlist_manipulation(id, **kwargs):
              
              auth_header = request.headers.get('Authorization')
              access_token = auth_header.split(" ")[1]
      
              if access_token:
                  
                  user_id = User.decode_token(access_token)
      
                  if not isinstance(user_id, str):
                      
                      
                      bucketlist = Bucketlist.query.filter_by(id=id).first()
                      if not bucketlist:
                          
                          
                          abort(404)
      
                      if request.method == "DELETE":
                          
                          bucketlist.delete()
                          return {
                              "message": "bucketlist {} deleted".format(bucketlist.id)
                          }, 200
      
                      elif request.method == 'PUT':
                          
                          name = str(request.data.get('name', ''))
      
                          bucketlist.name = name
                          bucketlist.save()
      
                          response = {
                              'id': bucketlist.id,
                              'name': bucketlist.name,
                              'date_created': bucketlist.date_created,
                              'date_modified': bucketlist.date_modified,
                              'created_by': bucketlist.created_by
                          }
                          return make_response(jsonify(response)), 200
                      else:
                          
                          response = {
                              'id': bucketlist.id,
                              'name': bucketlist.name,
                              'date_created': bucketlist.date_created,
                              'date_modified': bucketlist.date_modified,
                              'created_by': bucketlist.created_by
                          }
                          return make_response(jsonify(response)), 200
                  else:
                      
                      message = user_id
                      response = {
                          'message': message
                      }
                      
                      return make_response(jsonify(response)), 401
      
          
          from .auth import auth_blueprint
          app.register_blueprint(auth_blueprint)
      
          return app
      

      Running python manage.py test should now yield passing tests.

      1. test_already_registered_user (test_auth.AuthTestCase)
      2. Test that a user cannot be registered twice. ... ok
      3. test_non_registered_user_login (test_auth.AuthTestCase)
      4. Test non registered users cannot login. ... ok
      5. test_registration (test_auth.AuthTestCase)
      6. Test user registration works correcty. ... ok
      7. test_user_login (test_auth.AuthTestCase)
      8. Test registered user can login. ... ok
      9. test_api_can_get_all_bucketlists (test_bucketlist.BucketlistTestCase)
      10. Test API can get a bucketlist (GET request). ... ok
      11. test_api_can_get_bucketlist_by_id (test_bucketlist.BucketlistTestCase)
      12. Test API can get a single bucketlist by using it's id. ... ok
      13. test_bucketlist_can_be_edited (test_bucketlist.BucketlistTestCase)
      14. Test API can edit an existing bucketlist. (PUT request) ... ok
      15. test_bucketlist_creation (test_bucketlist.BucketlistTestCase)
      16. Test API can create a bucketlist (POST request) ... ok
      17. test_bucketlist_deletion (test_bucketlist.BucketlistTestCase)
      18. Test API can delete an existing bucketlist. (DELETE request). ... ok
      19. ----------------------------------------------------------------------
      20. Ran 9 tests in 1.579s
      21. OK

      Now let’s test to see if it works on Postman.

      Fire up the API using python run.py development

      Make a GET request for a single bucketlist to localhost:5000/bucketlists/2

      Feel free to play around with the PUT and DELETE functionality.

      We’ve covered quite a lot on securing our API. We went through defining a user model and integrating users into our API. We also covered token-based authentication and used an authentication blueprint to implement it.

      Even though our main focus is to write the code, we should not let testing be an afterthought.
      For us to improve on code quality, there have to be tests. Testing is the secret to increasing the agility of your product development. In everything project you do, put TTD first.

      If you’ve coded this to the end, you are awesome!

      Feel free to recommend this to friends and colleagues.

      Build a Single Page Time Tracking App with Vue.js: Introduction

      Introduction

      Vue.js is simple. It is so simple that people often dismiss it as only suitable for small projects. While it is true the Vue.js core is just a view layer library, there are in fact a collection of tools that will enable you to build full-blown, large-scale SPAs (Single Page Applications) using Vue.js with a pleasant development experience.

      If you are already familiar with the basics of Vue.js but feel that the world of SPA is scary, this series is for you. We will first introduce the concepts, tools, and libraries needed in this first article, and then will walk you through the full process of building an example app in the rest of the series.

      Single-Page Applications (SPAs) are web apps that load a single HTML page and dynamically update that page as the user interacts with the app. SPAs use AJAX and HTML5 to create fluid and responsive Web apps, without constant page reloads.

      As stated in the above description taken from Wikipedia, the main advantage of SPAs is that the app can respond to user interactions without fully reloading the page, resulting in a much more fluid user experience.

      As a nice side effect, a SPA also encourages the backend to focus on exposing data endpoints, which makes the overall architecture more decoupled and potentially reusable for other types of clients.

      From the developer’s perspective, the main difference between SPAs and a traditional backend-rendered app is that we have to treat the client-side as an application with its own structure. Typically we will need to handle routing, data fetching and persistence, view rendering, and the necessary build setup to facilitate a modularized codebase.

      For a Vue.js-based SPA, here are the tools and libraries that we will use to fill in these gaps:

      • View Layer: Vue.js, of course 🙂
      • Routing: vue-router, the official router for Vue.js.
      • State Management: vuex, a state-management solution inspired by Flux/Redux, but designed specifically for Vue.
      • Server Communication: vue-resource, a plugin for interfacing with a RESTful backend.
      • Build Tool: Webpack and vue-loader for modules, hot-reloading, ES2015, pre-processors and most importantly, single-file Vue components.

      Let’s take a closer look at each part.

      The View Layer

      This series assumes you are already familiar with the basics of Vue.js. If you are not, you should be able to quickly pick it up by going through official guide and other tutorials available.

      The core concept when using Vue.js for large SPAs is dividing your application into many nested, self-contained components. We also want to carefully design how these components interact with one another by leveraging component props for the data flow and custom events for communication. By doing so, we dissect the complexity into small, decoupled units that are tremendously easier to maintain.

      Routing

      The official vue-router library handles client-side routing, and supports both hash mode and HTML5 history mode. It is a bit different from standalone routing libraries in that it deeply integrates with Vue.js and makes the assumption that we are mapping nested routes to nested Vue components.

      When using vue-router, we implement components that serve as “pages”, and within these components we can implement hook functions that are called when the route changes.

      State Management

      State management is a topic that only arises when your application’s complexity grows beyond a certain level. When you have multiple components that need to share and mutate application state, it can get very hard to reason about and maintain if you don’t have a layer in your application that is dedicated to managing such shared state.

      This is where Vuex comes in. You don’t necessarily need Vuex if your application is relatively simple – but if you are interested, here’s an excellent intro on what problem it solves by Anirudh Sanjeev.

      Server Communication

      We will be working with a RESTful backend in the example, so we are using the vue-resource plugin which is maintained by the PageKit team. Do note that Vue.js SPAs are backend-agnostic and can basically work with any data fetching solution you prefer, for example fetch, restful.js, Firebase or even Falcor.

      Build Tool

      This is probably the biggest hurdle that you’ll have to jump through if you are not familiar with the frontend build tool scene, and we will try to explain it here. Feel free to skip this section if you are already experienced with Webpack.

      First, the entire build toolchain relies on Node.js, and we will be managing all our library and tool dependencies using npm. Although npm started out as the package manager for Node.js backend modules, it is now widely used for front-end package management, too. Because all npm packages are authored using the CommonJS module format, we need special tooling to “bundle” these modules into files that are suitable for final deployment. Webpack is exactly such a tool, and you may have also heard of a similar tool called Browserify.

      We will be using Webpack for the series because it provides more advanced functionalities out of the box, such as hot-reloading, bundle-splitting, and static asset handling.

      Both Webpack and Browserify exposes APIs that allow us to load more than just CommonJS modules: for example, we can directly require() an HTML file by transforming it into a JavaScript string.

      By treating everything for your frontend including HTML, CSS, and even image files as module dependencies that can be arbitrarily transformed during the bundling process, Webpack actually covers most of the build tasks that you will encounter when building a SPA. We are primarily going to build the example using Webpack and plain NPM scripts, without the need for a task runner like Gulp or Grunt.

      We will also be using vue-loader which enables us to author Vue components in a single file:

      app.vue

      <template>
        <h1 class="red">{{msg}}</h1>
      </template>
      
      <script>
      export default {
        data () {
          return {
            msg: 'Hello world!'
          }
        }
      }
      </script>
      
      <style>
      .red {
        color: #f00;
      }
      </style>
      

      In addition, the combination of Webpack and vue-loader gives us:

      1. ES2015 by default. This allows us to use future JavaScript syntax today, which results in more expressive and concise code.

      2. Embedded pre-processors. You can use your pre-processors of choice inside single-file Vue components, for example using Jade for the template and SASS for the styles.

      3. CSS output inside Vue components are auto-prefixed. You can also use any PostCSS plugins you like.

      4. Scoped CSS. By adding a scoped attribute to the <style>, vue-loader will simulate scoped CSS by rewriting the template and style output so that the CSS for a specific component will not affect other parts of your app.

      5. Hot Reload. When editing a Vue component during development, the component will be “hot-swapped” into the running app, maintaining the app state without having the reload the page. This greatly improves the development experience.

      Now with all these fancy features, it could be a really daunting task to assemble the build stack yourself! Luckily, Vue provides vue-cli, a command-line interface that makes it trivially easy to get started:

      1. npm install -g vue-cli
      1. vue init webpack my-project

      Answer the prompts, and the CLI will scaffold a project with all the aforementioned features working out of the box. All you need to do next is:

      1. cd my-project
      2. npm install
      3. npm run dev

      For full details on what is included in the generated project, check out the project template documentation.

      We haven’t really written any real app code so far, but I hope we have got you excited about learning more.

      In the next article, Ryan Chenkie will start taking us through a series of building a full-fledged SPA using this stack. Stay tuned!

      Build a Custom JavaScript Scrollspy Navigation

      Introduction

      The content of an HTML document can be very long and difficult to access only through the scroll. Because of this arduous task, developers often use internal links (page jumps) as an alternative mode of transport around the page. This useful technique has been improved with the help of Javascript to offer a better experience, primarily by offering soft jumps and then introducing the so-called Scrollspy scripts.

      A Scrollspy is used to automatically update links in a navigation list based on scroll position.

      Through this tutorial, we’ll be building a custom Scrollspy component. See exactly what we are going to build below:

      Custom Scrollspy

      Also, you can take a look at the working DEMO.

      To accomplish this custom Scrollspy we will be using:

      • Gumshoe: Simple, framework-agnostic scrollspy script.
      • Smooth Scroll: Lightweight script to animate scrolling to anchor links.
      • Anime.js: Flexible yet lightweight Javascript animation library.

      Along with the tutorial, we’ll be explaining some features we use of these libraries, but it’s a good idea to check the Github repositories, for basic understanding.

      Let’s start with the HTML structure we’ll be using, describing the key elements in the comments:

      <section>
          
          
          
          <header class="page-header" data-gumshoe-header data-scroll-header>
              <div class="page-nav">
                  
                  
                  <nav data-gumshoe>
                      
                      <a data-scroll href="#eenie">Eenie</a>
                      <a data-scroll href="#meanie">Meanie</a>
                      <a data-scroll href="#minnie">Minnie</a>
                      <a data-scroll href="#moe">Moe</a>
                  </nav>
                  
                  <a class="nav-arrow nav-arrow-left"><svg class="icon"><use xlink:href="#arrow-up"/></svg></a>
                  <a class="nav-arrow nav-arrow-right"><svg class="icon"><use xlink:href="#arrow-down"/></svg></a>
              </div>
          </header>
          
          <main class="page-content">
              <section>
                  <h2 id="eenie"><a data-scroll href="#eenie">Eenie</a></h2>
                  <p>Lorem ipsum dolor sit amet, has dico eligendi ut.</p>
                  
              </section>
          </main>
      </section>
      

      With the HTML ready, we are all set to add some style. Let’s see the key style pieces commented briefly:

      h2 {
        
        
        &:before {
          display: block;
          content: " ";
          margin-top: -110px;
          height: 110px;
          visibility: hidden;
        }
      }
      
      
      .page-header {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 80px; 
        background-color: #2D353F;
        text-align: center;
        z-index: 2;
      }
      
      
      .page-content {
        display: inline-block; 
        margin: 80px 50px 30px; 
      }
      
      
      .page-nav {
        display: inline-block;
        position: relative;
        margin-top: 20px;
        height: 40px; 
        width: 400px;
        max-width: 100%; 
        overflow: hidden; 
        background-color: #427BAB;
      }
      
      
      nav {
        position: relative;
        width: 100%;
        line-height: 40px;
        text-align: center;
        background-color: rgba(0, 0, 0, 0.05);
      
        a {
          display: block;
          font-size: 18px;
          color: #fff;
          outline: none;
        }
      }
      

      As we will be working closely with the DOM, we need to get all the elements we need first. Also, we will declare the additional variables we will be using.

      
      var navOpen = false;
      var pageNav = document.querySelector('.page-nav');
      var navEl = document.querySelector('.page-nav nav');
      var navLinks = document.querySelectorAll('.page-nav nav a');
      var arrowLeft = document.querySelector('.nav-arrow-left');
      var arrowRight = document.querySelector('.nav-arrow-right');
      var navHeight = 40;
      var activeIndex, activeDistance, activeItem, navAnimation, navItemsAnimation;
      

      The following is a key part of the puzzle. This function translates the nav element to show only the selected link, using the activeIndex value.

      
      function translateNav(item) {
          
          if (navItemsAnimation) navItemsAnimation.pause();
          
          navItemsAnimation = anime({
              targets: navEl,
              translateY: (item ? -activeIndex * navHeight : 0) + 'px',
              easing: 'easeOutCubic',
              duration: 500
          });
          
          updateArrows();
      }
      

      Then, we need a way to open and close the nav. The open state should let us see all the links and allow us to select one of them directly. The close state is the default one, letting see only the selected link.

      
      function openNav() {
          
          navOpen = !navOpen;
          pageNav.classList.add('nav-open');
          
          translateNav();
          
          navAnimation = anime({
              targets: pageNav,
              height: navLinks.length * navHeight + 'px',
              easing: 'easeOutCubic',
              duration: 500
          });
      }
      
      
      function closeNav() {
          
          navOpen = !navOpen;
          pageNav.classList.remove('nav-open');
          
          translateNav(activeItem);
          
          navAnimation = anime({
              targets: pageNav,
              height: navHeight + 'px',
              easing: 'easeOutCubic',
              duration: 500
          });
      }
      

      Now let’s see how we handle the events. We need handlers to open or close the nav accordingly.

      
      for (var i = 0; i < navLinks.length; i++) {
          navLinks[i].addEventListener('click', function (e) {
              if (navOpen) {
                  
                  closeNav();
              } else {
                  
                  e.preventDefault();
                  e.stopPropagation();
                  openNav();
              }
          });
      }
      
      
      
      document.addEventListener('click', function (e) {
          if (navOpen) {
              var isClickInside = pageNav.contains(e.target);
              if (!isClickInside) {
                  closeNav();
              }
          }
      });
      

      We are ready to let Gumshoe and Smooth Scroll do the magic. See how we are initializing them:

      
      smoothScroll.init({
          
          offset: -80
      });
      
      
      gumshoe.init({
          
          callback: function (nav) {
              
              if (activeDistance !== nav.distance) {
                  
                  activeDistance = nav.distance;
                  activeItem = nav.nav;
                  activeIndex = getIndex(activeItem);
                  
                  if (navOpen) {
                      closeNav();
                  } else {
                      translateNav(activeItem);
                  }
              }
          }
      });
      

      Conclusion

      And we are done! You can see it working here.

      For the sake of clarity, we have commented only the most important parts of the code. But you can get it all from this GitHub repo.

      We really hope you have enjoyed it and found it useful!