One place for hosting & domains

      URL

      How To Make a URL Shortener with Flask and SQLite


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Flask is a framework for building web applications using Python and SQLite is a database engine that you can use with Python to store application data.

      In this tutorial, you will build a URL shortener, a service that takes any URL and generates a shorter, more readable version like bit.ly.

      Hashids is a library that generates a short unique ID from integers. For example, you can use it to convert a number like 12 to a unique string like 1XcId. You will use Hashids to generate unique strings for URL IDs.

      You can use unique strings to generate IDs for videos on a video-sharing site or IDs for images on a service to upload images. This unique string gives you unpredictable IDs; therefore, if a user can access an image at your_domain/image/J32Fr, they can’t predict the location of other images. This is not possible if you use integer IDs in a URL shortener—for example, your_domain/image/33 would allow users to predict the location of other images. Unpredictable URLs add a form of privacy to your service because they prevent users from working out different URLs shortened by other users.

      You will use Flask, SQLite, and the Hashids library to build your URL shortener. Your application will allow users to enter a URL and generate a shorter version, in addition to a statistics page where users can view the number of times a URL has been clicked. You’ll use the Bootstrap toolkit to style your application.

      Prerequisites

      Step 1 — Setting Up Dependencies

      In this step, you will activate your Python environment and install Flask and the Hashids library using the pip package installer. Then you’ll create the database you will use to store URLs.

      First, activate your programming environment if you haven’t already:

      Once you have activated your programming environment, install Flask and the Hashids library using the following command:

      • pip install flask hashids

      Then create a database schema file called schema.sql, containing SQL commands to create a urls table. Open a file called schema.sql inside your flask_shortener directory:

      Type the following SQL commands inside this file:

      flask_shortener/schema.sql

      DROP TABLE IF EXISTS urls;
      
      CREATE TABLE urls (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          original_url TEXT NOT NULL,
          clicks INTEGER NOT NULL DEFAULT 0
      );
      

      In the schema file, you first delete the urls table if it already exists. This avoids the possibility of another table named urls existing, which might result in confusing behavior; for example, if it has different columns. Note that this will delete all of the existing data whenever the schema file executes.

      You then create the table with the following columns:

      • id: The ID of the URL, this will be a unique integer value for each URL entry. You will use it to get the original URL from a hash string.
      • created: The date the URL was shortened.
      • original_url: The original long URL to which you will redirect users.
      • clicks: The number of times a URL has been clicked. The initial value will be 0, which will increment with each redirect.

      Save and close the file.

      To execute the schema.sql file to create the urls table, open a file named init_db.py inside your flask_shortener directory:

      Then add the following code:

      flask_shortener/init_db.py

      import sqlite3
      
      connection = sqlite3.connect('database.db')
      
      with open('schema.sql') as f:
          connection.executescript(f.read())
      
      connection.commit()
      connection.close()
      

      Here you connect to a file called database.db that your program will create once you execute this program. This file is the database that will hold all of your application’s data. You then open the schema.sql file and run it using the executescript() method that executes multiple SQL statements at once. This will create the urls table. Finally, you commit the changes and close the connection.

      Save and close the file.

      Run the program:

      After execution, a new file called database.db will appear in your flask_shortener directory.

      With this, you’ve installed Flask and the Hashids library, created the database schema, and created the SQLite database with a table called urls to store the URL shortener’s original URLs. Next, you’ll use Flask to create the index page where your users can enter a URL to generate a short URL.

      Step 2 — Creating the Index Page for Shortening URLs

      In this step, you will create a Flask route for the index page, which will allow users to enter a URL that you then save into the database. Your route will use the ID of the URL to generate a short string hash with the Hashids library, construct the short URL, and then render it as a result.

      First, open a file named app.py inside your flask_shortener directory. This is the main Flask application file:

      Add the following code to the file:

      flask_shortener/app.py

      import sqlite3
      from hashids import Hashids
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      

      In this code, you first import the sqlite3 module, the Hashids class from the hashids library, and Flask helpers.

      The get_db_connection() function opens a connection to the database.db database file and then sets the row_factory attribute to sqlite3.Row. As a result, you can have name-based access to columns; the database connection will return rows that behave like regular Python dictionaries. Lastly, the function returns the conn connection object you’ll be using to access the database.

      Next, add the following:

      flask_shortener/app.py

      . . .
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      
      hashids = Hashids(min_length=4, salt=app.config['SECRET_KEY'])
      
      

      You create the Flask application object and set a secret key to secure sessions. Since the secret key is a secret random string, you’ll also use it to specify a salt for the Hashids library; this will ensure the hashes are unpredictable since every time the salt changes, the hashes also change.

      Note: A salt is a random string that is provided to the hashing function (that is, hashids.encode()) so that the resulting hash is shuffled based on the salt. This process ensures the hash you get is specific to your salt so that the hash is unique and unpredictable, like a secret password that only you can use to encode and decode hashes. Remember to keep it secret for security purposes (which is why you use the application’s secret key).

      You create a hashids object specifying that a hash should be at least 4 characters long by passing a value to the min_length parameter. You use the application’s secret key as a salt.

      Next, add the following code to the end of your file:

      flask_shortener/app.py

      . . .
      @app.route('/', methods=('GET', 'POST'))
      def index():
          conn = get_db_connection()
      
          if request.method == 'POST':
              url = request.form['url']
      
              if not url:
                  flash('The URL is required!')
                  return redirect(url_for('index'))
      
              url_data = conn.execute('INSERT INTO urls (original_url) VALUES (?)',
                                      (url,))
              conn.commit()
              conn.close()
      
              url_id = url_data.lastrowid
              hashid = hashids.encode(url_id)
              short_url = request.host_url + hashid
      
              return render_template('index.html', short_url=short_url)
      
          return render_template('index.html')
      

      The index() functions is a Flask view function, which is a function decorated using the special @app.route decorator. Its return value gets converted into an HTTP response that an HTTP client, such as a web browser, displays.

      Inside the index() view function, you accept both GET and POST requests by passing methods=('GET', 'POST') to the app.route() decorator. You open a database connection.

      Then if the request is a GET request, it skips the if request.method == 'POST' condition until the last line. This is where you render a template called index.html, which will contain a form for users to enter a URL to shorten.

      If the request is a POST request, the if request.method == 'POST' condition is true, which means a user has submitted a URL. You store the URL in the url variable; if the user has submitted an empty form, you flash the message The URL is required! and redirect to the index page.

      If the user has submitted a URL, you use the INSERT INTO SQL statement to store the submitted URL in the urls table. You include the ? placeholder in the execute() method and pass a tuple containing the submitted URL to insert data safely into the database. Then you commit the transaction and close the connection.

      In a variable called url_id, you store the ID of the URL you inserted into the database. You can access the ID of the URL using the lastrowid attribute, which provides the row ID of the last inserted row.

      You construct a hash using the hashids.encode() method, passing it the URL ID; you save the result in a variable called hashid. As an example, the call hashids.encode(1) might result in a unique hash like KJ34 depending on the salt you use.

      You then construct the short URL using request.host_url, which is an attribute that Flask’s request object provides to access the URL of the application’s host. This will be http://127.0.0.1:5000/ in a development environment and your_domain if you deploy your application. For example, the short_url variable will have a value like http://127.0.0.1:5000/KJ34, which is the short URL that will redirect your users to the original URL stored in the database with the ID that matches the hash KJ34.

      Lastly, you render the index.html template passing the short_url variable to it.

      After all the additions, the file will be as follows:

      flask_shortener/app.py

      import sqlite3
      from hashids import Hashids
      from flask import Flask, render_template, request, flash, redirect, url_for
      
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'this should be a secret random string'
      
      hashids = Hashids(min_length=4, salt=app.config['SECRET_KEY'])
      
      
      @app.route('/', methods=('GET', 'POST'))
      def index():
          conn = get_db_connection()
      
          if request.method == 'POST':
              url = request.form['url']
      
              if not url:
                  flash('The URL is required!')
                  return redirect(url_for('index'))
      
              url_data = conn.execute('INSERT INTO urls (original_url) VALUES (?)',
                                      (url,))
              conn.commit()
              conn.close()
      
              url_id = url_data.lastrowid
              hashid = hashids.encode(url_id)
              short_url = request.host_url + hashid
      
              return render_template('index.html', short_url=short_url)
      
          return render_template('index.html')
      

      Save and close the file.

      Next, you’ll create a base template and the index.html template file.

      In your flask_shortener directory, create a templates directory and open a file called base.html inside it:

      • mkdir templates
      • nano templates/base.html

      Add the following code inside base.html. Note that, for styling, you’re using Bootstrap here too. If you are not familiar with HTML templates in Flask, see Step 3 of How To Make a Web Application Using Flask in Python 3:

      flask_shortener/templates/base.html

      <!doctype html>
      <html lang="en">
        <head>
          <!-- Required meta tags -->
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      
          <!-- Bootstrap CSS -->
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskShortener</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% for message in get_flashed_messages() %}
                  <div class="alert alert-danger">{{ message }}</div>
              {% endfor %}
              {% block content %} {% endblock %}
          </div>
      
          <!-- Optional JavaScript -->
          <!-- jQuery first, then Popper.js, then Bootstrap JS -->
          <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Most of the code in the preceding block is standard HTML and code required for Bootstrap. The <meta> tags provide information for the web browser, the <link> tag links the Bootstrap CSS files, and the <script> tags are links to JavaScript code that allows some additional Bootstrap features. Check out the Bootstrap documentation for more information.

      The <title>{% block title %} {% endblock %}</title> tag allows the inheriting templates to define a custom title. You use the for message in get_flashed_messages() loop to display the flashed messages (warnings, alerts, and so on). The {% block content %} {% endblock %} placeholder is where inheriting templates place the content so that all templates have access to this base template, which avoids repetition.

      Save and close the file.

      Next, create the index.html file that will extend this base.html file:

      • nano templates/index.html

      Add the following code to it:

      flask_shortener/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskShortener {% endblock %}</h1>
          <form method="post">
          <div class="form-group">
              <label for="url">URL</label>
              <input type="text" name="url"
                     placeholder="URL to shorten" class="form-control"
                     value="{{ request.form['url'] }}" autofocus></input>
          </div>
      
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
          </form>
      
          {% if short_url %}
          <hr>
          <span>{{ short_url }}</span>
          {% endif %}
      {% endblock %}
      

      Here you extend base.html, define a title, and create a form with an input named url. The url input will allow users to enter URLs to shorten. It has a value of request.form['url'], which stores data in cases of submission failure; that is if the user provides no URL. You also add a submit button.

      Then you check if the short_url variable has any value—this is true if the form submits and the short URL generates successfully. If the condition is true, you display the short URL under the form.

      Set the environment variables Flask needs and run the application using the following commands:

      • export FLASK_APP=app
      • export FLASK_ENV=development
      • flask run

      The FLASK_APP environment variable specifies the application you want to run (the app.py file). The FLASK_ENV environment variable specifies the mode. development means that the application will run in development mode with the debugger running. Remember to avoid using this mode in production. You run the application using the flask run command.

      Open a browser and type in the URL http://127.0.0.1:5000/. You will find a Welcome to FlaskShortener page.

      Flask Shortener Index page

      Submit a URL, and you will receive a short URL.

      Flask Shortened URL displayed beneath the URL input box

      You created a Flask application with a page that accepts URLs and generates shorter ones, but the URLs don’t do anything yet. In the next step, you’ll add a route that extracts the hash from the short URL, finds the original URL, and redirects users to it.

      Step 3 — Adding the Redirect Route

      In this step, you will add a new route that takes the short hash the application generates and decodes the hash into its integer value, which is the original URL’s ID. Your new route will also use the integer ID to fetch the original URL and increment the clicks value. Finally, you will redirect users to the original URL.

      First, open the app.py to add a new route:

      Add the following code to the end of the file:

      flask_shortener/app.py

      . . .
      
      @app.route('/<id>')
      def url_redirect(id):
          conn = get_db_connection()
      
          original_id = hashids.decode(id)
          if original_id:
              original_id = original_id[0]
              url_data = conn.execute('SELECT original_url, clicks FROM urls'
                                      ' WHERE id = (?)', (original_id,)
                                      ).fetchone()
              original_url = url_data['original_url']
              clicks = url_data['clicks']
      
              conn.execute('UPDATE urls SET clicks = ? WHERE id = ?',
                           (clicks+1, original_id))
      
              conn.commit()
              conn.close()
              return redirect(original_url)
          else:
              flash('Invalid URL')
              return redirect(url_for('index'))
      

      This new route accepts a value id through the URL and passes it to the url_redirect() view function. For example, visiting http://127.0.0.1:5000/KJ34 would pass the string 'KJ34' to the id parameter.

      Inside the view function, you first open a database connection. Then you use the decode() method of the hashids object to convert the hash to its original integer value and store it in the original_id variable. You check that the original_id has a value—meaning decoding the hash was successful. If it has a value, you extract the ID from it. As the decode() method returns a tuple, you fetch the first value in the tuple with original_id[0], which is the original ID.

      You then use the SELECT SQL statement to fetch the original URL and its number of clicks from the urls table, where the ID of the URL matches the original ID you extracted from the hash. You fetch the URL data with the fetchone() method. Next, you extract the data into the two original_url and clicks variables.

      You then increment the number of clicks of the URL with the UPDATE SQL statement.

      You commit the transaction and close the connection, and redirect to the original URL using the redirect() Flask helper function.

      If decoding the hash fails, you flash a message to inform the user that the URL is invalid, and redirect them to the index page.

      Save and close the file.

      Run your development server:

      Use your browser to go to http://127.0.0.1:5000/. Enter a URL and visit the resulting short URL; your application will redirect you to the original URL.

      You created a new route that redirects users from the short URL to the original URL. Next, you’ll add a page to show how many times each URL has been visited.

      Step 4 — Adding a Statistics Page

      In this step, you’ll add a new route for a statistics page that displays how many times each URL has been clicked. You’ll also add a button that links to the page on the navigation bar.

      Allowing users to see the number of visits each shortened link has received will provide visibility into each URL’s popularity, which is useful for projects, like marketing ad campaigns. You can also use this workflow as an example of adding a feature to an existing Flask application.

      Open app.py to add a new route for a statistics page:

      Add the following code to the end of the file:

      flask_shortener/app.py

      . . .
      
      @app.route('/stats')
      def stats():
          conn = get_db_connection()
          db_urls = conn.execute('SELECT id, created, original_url, clicks FROM urls'
                                 ).fetchall()
          conn.close()
      
          urls = []
          for url in db_urls:
              url = dict(url)
              url['short_url'] = request.host_url + hashids.encode(url['id'])
              urls.append(url)
      
          return render_template('stats.html', urls=urls)
      

      In this view function, you open a database connection. Then you fetch the ID, the creation date, the original URL, and the number of clicks for all of the entries in the urls table. You use the fetchall() method to get a list of all the rows. You then save this data in the db_urls variable and close the connection.

      To display the short URL for each entry, you will need to construct it and add it to each item in the list of the URLs you fetched from the database (db_urls). You create an empty list called urls and loop through the db_urls list with for url in db_urls.

      You use the dict() Python function to convert the sqlite3.Row object to a dictionary to allow assignment. You add a new key called short_url to the dictionary with the value request.host_url + hashids.encode(url['id']), which is what you used before to construct short URLs in the index view function. You append this dictionary to the urls list.

      Finally, you render a template file called stats.html, passing the urls list to it.

      Save and close the file.

      Next, create the new stats.html template file:

      • nano templates/stats.html

      Type the following code into it:

      flask_shortener/templates/stats.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} FlaskShortener Statistics {% endblock %}</h1>
          <table class="table">
              <thead>
                  <tr>
                  <th scope="col">#</th>
                  <th scope="col">Short</th>
                  <th scope="col">Original</th>
                  <th scope="col">Clicks</th>
                  <th scope="col">Creation Date</th>
                  </tr>
              </thead>
              <tbody>
                  {% for url in urls %}
                      <tr>
                          <th scope="row">{{ url['id'] }}</th>
                          <td>{{ url['short_url'] }}</td>
                          <td>{{ url['original_url'] }}</td>
                          <td>{{ url['clicks'] }}</td>
                          <td>{{ url['created'] }}</td>
                      </tr>
                  {% endfor %}
              </tbody>
          </table>
      
      {% endblock %}
      

      Here you extend the base.html base template by specifying a title and defining a table with the following columns:

      • #: The ID of the URL.
      • Short: The short URL.
      • Original: The original URL.
      • Clicks: The number of times a short URL has been visited.
      • Creation Date: The creation date of the short URL.

      Each row is filled using a for loop that goes through the urls list and displays the value of each column for each URL.

      Run the development server with the following:

      Use your browser to go to http://127.0.0.1:5000/stats. You will find all the URLs in a table.

      Statistics page with list of URLs and number of clicks

      Next, add a Stats button to the navigation bar. Open the base.html file:

      Edit the file as per the following highlighted lines:

      flask_shortener/templates/base.html

      <!doctype html>
      <html lang="en">
        <head>
          <!-- Required meta tags -->
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      
          <!-- Bootstrap CSS -->
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/community/tutorials/{{ url_for("index')}}">FlaskTodo</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="#">About</a>
                  </li>
      
                  <li class="nav-item active">
                      <a class="nav-link" href="https://www.digitalocean.com/community/tutorials/{{ url_for("stats')}}">Stats</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% for message in get_flashed_messages() %}
                  <div class="alert alert-danger">{{ message }}</div>
              {% endfor %}
              {% block content %} {% endblock %}
          </div>
      
          <!-- Optional JavaScript -->
          <!-- jQuery first, then Popper.js, then Bootstrap JS -->
          <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Here you incorporate a new <li> item to the navigation bar. You use the url_for() function to link to the stats() view function. You can now access the statistics page from the navigation bar.

      Your statistics page shows information about each URL, including its shorter equivalent and how many times it has been visited.

      You can reuse this code for monitoring number of clicks in other contexts, such as keeping track of how many times a post has been liked or updated on a social media site or how many times a photo/video has been viewed.

      You can access the full code for the application from this repository.

      Conclusion

      You have created a Flask application that allows users to enter a long URL and generate a shorter version. You have transformed integers into short string hashes, redirected users from one link to another, and set up a page for statistics so you can monitor shortened URLs. For further projects and tutorials on working with Flask, check out the following tutorials:



      Source link

      Use ExpressJS to Get URL and POST Parameters


      While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or
      edited it to ensure you have an error-free learning experience. It’s on our list, and we’re working on it!
      You can help us out by using the “report an issue” button at the bottom of the tutorial.

      Often when we are building applications using ExpressJS, we will need to get information from our users.

      This can happen any number of ways but the two most popular are:

      URL Parameters

      These are information that are passed through the URL like so:

      http://example.com/api/users?id=4&token=sdfa3&geo=us
      

      This is most seen in requesting information from an API.

      URL Parameters are grabbed using req.param.variable_name

      POST Parameters

      These are information that come from forms. We’ll be grabbing from forms that pass information as application/x-www-form-urlencoded.

      POST Parameters are grabbed using req.body.variable_name

      Let’s take a look at how we can grab the parameters using the popular Node.js framework, ExpressJS.

      Sample App to Test

      We’ll be creating a sample application to make sure that grabbing parameters works. We’ll also be using POSTman to test the form POST.

      We’ll only need two files:

      - package.json
      - server.js
      

      Here is the package.json

      {
          "name": "express-parameters",
          "main": "server.js",
          "dependencies": {
          "express": "~4.10.2"
          }
      }
      

      Here’s the start of our server.js:

      // grab the packages we need
      var express = require('express');
      var app = express();
      var port = process.env.PORT || 8080;
      
      // routes will go here
      
      // start the server
      app.listen(port);
      console.log('Server started! At http://localhost:' + port);
      

      We can now start our server using:

      You will have to restart the node server every time you edit server.js. If this gets tedious, see how To Restart Your Node.js Apps Automatically with nodemon.

      Now let’s create two routes now to test grabbing parameters.

      Getting Any URL Parameter

      For this example, we’ll grab a parameter directly from the URL. Let’s say we are using the example URL: http://example.com/api/users?id=4&token=sdfa3&geo=us

      The parameters that come out of this will be:

      id: 4
      token: sdfa3
      geo: us
      

      It’s very easy to grab these parameters. Here’s the route and the method for grabbing parameters is req.param().

      // routes will go here
      app.get('/api/users', function(req, res) {
          var user_id = req.param('id');
          var token = req.param('token');
          var geo = req.param('geo');  
      
          res.send(user_id + ' ' + token + ' ' + geo);
      });
      

      The parameters are naturally passed through the req (request) part. Now if we go to our browser at http://localhost:8080/api/users?id=4&token=sdfa3&geo=us, we’ll be able to see the three parameters!

      Specific Routing for Specific Parameters

      We can also name the paramter directly in the route itself. Here’s a URL and example:

      // http://localhost:8080/api/1
      app.get('/api/:version', function(req, res) {
          res.send(req.params.version);
      });
      

      Route Parameter Middleware

      Next up, we’ll be using the Express param function to grab a specific parameter. This is considered middleware and will run before the route is called.

      This can be used for validations (like checking if a user exists) or grabbing important information about that user or item.

      An example for this would be:

      // parameter middleware that will run before the next routes
      app.param('name', function(req, res, next, name) {
      
          // check if the user with that name exists
          // do some validations
          // add -dude to the name
          var modified = name + '-dude';
      
          // save name to the request
          req.name = modified;
      
          next();
      });
      
      // http://localhost:8080/api/users/chris
      app.get('/api/users/:name', function(req, res) {
          // the user was found and is available in req.user
          res.send('What is up ' + req.name + '!');
      });
      

      If we visit the URL in our browser, we’ll see:

      What is up chris-dude!

      Pretty neat. You can use this param middleware for validations and making sure that information passed through is valid and the correct format.

      We then save information to the request (req) so that our other route will have access to it.

      Further Reading More Express routing: Learn to Use the Express 4.0 Router

      POST Parameters

      To get POST parameters, we’ll need two the ExpressJS body-parser package. This will allow us to grab information from the POST.

      Install body-parser

      We’ll need to install body-parser using:

      • npm install body-parser --save

      Now that we have that package and it has been added to our package.json thanks to the --save modifier, we have to configure it in our server.js file.

      Add the following after you define the app variable.

      var bodyParser = require('body-parser');
      app.use(bodyParser.json()); // support json encoded bodies
      app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
      

      With body-parser configured, we can now create our route:

      We will grab POST parameters using req.body.variable_name

      // POST http://localhost:8080/api/users
      // parameters sent with 
      app.post('/api/users', function(req, res) {
          var user_id = req.body.id;
          var token = req.body.token;
          var geo = req.body.geo;
      
          res.send(user_id + ' ' + token + ' ' + geo);
      });
      

      We can test this using POSTman and send information as application/x-www-form-urlencoded:

      Conclusion

      Those are the quick ways we can grab information using ExpressJS. Grabbing data from the request is an easy enough process.

      Just grab the information quick and easily so that you can move on to building great things!



      Source link

      Cómo crear un acortador de URL con Django y GraphQL


      El autor seleccionó Girls Who Code para recibir una donación como parte del programa Write for DOnations.

      Introducción

      GraphQL es un estándar de API de código abierto creado por Facebook como alternativa a las API REST. En vez de las API REST, GraphQL utiliza un sistema escrito para definir su estructura de datos, donde toda la información enviada y recibida debe cumplir con un Schema predefinido. También expone un endpoint individual para todas las comunicaciones en vez de múltiples URLs para diferentes recursos y resuelve el problema de overfetching devolviendo solo los datos pedidos por el cliente, generando así respuestas más pequeñas y concisas.

      En este tutorial creará un backend para un servicio acortador de URL que toma cualquier URL y genera una versión más corta y legible. Además profundizaremos en los conceptos de GraphQL, como “Query” y “Mutation”, y en las herramientas como la interfaz GraphiQL. Es posible que ya haya usado dichos servicios antes, como bit.ly.

      Ya que GraphQL es un lenguaje con tecnología agnóstica, se implemente sobre varios lenguajes y marcos. Aquí, usará el lenguaje de programación Python de uso general, el marco web Django, y la biblioteca Graphene-Django como la implementación de GraphQL Python con integraciones específicas para Django.

      Requisitos previos

      Paso 1: Configurar el proyecto Django

      En este paso, instalará todas las herramientas necesarias para la aplicación y para configurar su proyecto de Django.

      Una vez que haya creado el directorio de su proyecto e iniciado su entorno virtual, como se indica en los requisitos previos, instale los paquetes necesarios usando pip, el administrador de paquetes de Phython. Este tutorial instalará la versión 2.1.7 de Django y la versión 2.2.0 de Graphene-Django o superior.

      • pip install "django==2.1.7" "graphene-django>==2.2.0"

      Ahora tendrá todas las herramientas necesarias para trabajar. A continuación, creará un proyecto Django usando el comando django-admin. Un proyecto es la plantilla predeterminada de Django, un conjunto de carpetas y archivos con todo lo necesario para iniciar el desarrollo de una aplicación web. En este caso, llamará a su proyecto shorty y lo creará dentro de su carpeta especificando el . al final:

      • django-admin startproject shorty .

      Tras crear su proyecto, ejecutará las migraciones de Django. Estos archivos contienen código de Python generado por Django y se encargan de cambiar la estructura de la aplicación según los modelos de Django. Los cambios pueden incluir la creación de una tabla, por ejemplo. Por defecto, Django cuenta con su propio conjuntos de migraciones responsables de los subsistemas como la Autenticación de Django, de forma que es necesario ejecutarlos con el siguiente comando:

      Este comando utiliza el intérprete de Python para invocar una secuencia de comandos llamada manage.py, responsable de gestionar los diferentes aspectos de su proyecto, como crear aplicaciones o ejecutar migraciones.

      Con esto, se mostrará un resultado similar al siguiente:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK

      Una vez que la base de datos de Django esté lista, inicie su servidor de desarrollo local:

      • python manage.py runserver

      Esto proporcionará lo siguiente:

      Output

      Performing system checks... System check identified no issues (0 silenced). March 18, 2020 - 15:46:15 Django version 2.1.7, using settings 'shorty.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.

      Este comando eliminará la instrucción en su terminal e iniciará el servidor.

      Visite la página http://127.0.0.1:8000 en su navegador local. Verá esta página:

      Página frontal del servidor local de Django

      Para detener el servidor y volver a su terminal, pulse CTRL+C. Siempre que necesite acceder al navegador, asegúrese de que se esté ejecutando el comando anterior.

      A continuación, terminará este paso habilitando la biblioteca Django-Graphene en el proyecto. Django tiene el concepto de app, una aplicación web con una responsabilidad específica. Un proyecto se compone de una o múltiples aplicaciones. Por ahora, abra el archivo shorty/settings.py​​​ en el editor de texto que prefiera. En este tutorial usaremos vim:

      El archivo settings.py gestiona todos los ajustes de su proyecto. Dentro, busque la entrada INSTALLED_APPS y añada la línea 'graphene_django':

      shorty/shorty/settings.py

      ...
      INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'graphene_django',
      ]
      ...
      

      Esta adición indica a Django que usará una aplicación llamada graphene_django, que instaló en el paso 1.

      Al final del archivo, añada la siguiente variable:

      shorty/shorty/settings.py

      ...
      GRAPHENE = {
          'SCHEMA': 'shorty.schema.schema',
      }
      

      Esta última variable apunta a su esquema principal. Lo creará más tarde. En GraphQL, un Schema contiene todos los tipos de objeto, como “Resource”, “Query” y “Mutation”. Piense en ello como la documentación que representa todos los datos y funcionalidades disponibles en su sistema.

      Tras realizar las modificaciones, guarde y cierre el archivo.

      Con esto, habrá configurado el proyecto Django. En el siguiente paso, creará una aplicación Django y sus Modelos.

      Paso 2: Configurar una aplicación y modelos Django

      Una plataforma Django está normalmente compuesta de un proyecto y muchas aplicaciones o apps. Una app describe un conjunto de funciones dentro de un proyecto, y, si está bien diseñada, puede reutilizarse en varios proyectos Django.

      En este paso, creará una app llamada shortener, responsable de la función real de acortamiento de URL. Para crear su esqueleto básico, escriba el siguiente comando en su terminal:

      • python manage.py startapp shortener

      Aquí usó los parámetros startapp app_name​​​, que indican a manage.py que cree una app llamada shortener.

      Para terminar de crear la app, abra el archivo shorty/settings.py​​​.

      Añada el nombre de la app a la misma entrada INSTALLED_APPS que modificó antes:

      shorty/shorty/settings.py

      ...
      INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'graphene_django'
          'shortener',
      ]
      ...
      

      Guarde y cierre el archivo.

      Una vez que se añada su shortener a shorty/settings.py, podrá proceder a crear los modelos para su proyecto. Los modelos son una de las funciones clave de Django. Se usan para representar una base de datos “al estilo de Phyton”, lo cual le permite administrar, consultar y almacenar datos usando código Python.

      Antes de abrir el archivo models.py para realizar cambios, en este tutorial se le proporcionará una descripción general de los cambios que realizará.

      Su archivo modelo, shortener/models.py, contendrá el siguiente contenido una vez que haya sustituido el código existente:

      shorty/shortener/models.py

      from hashlib import md5
      
      from django.db import models
      

      Aquí importará los paquetes requeridos que su código necesita. Añadirá la línea from hashlib import md5 encima para importar la biblioteca estándar Python que se usará para crear un hash de la URl. La línea from django.db import models es un elemento auxiliar de Django para crear modelos.

      Advertencia: Este tutorial se refiere a hash como el resultado de una función que toma una entrada y siempre devuelve el mismo resultado. Este tutorial usará la función hash MD5 con fines demostrativos.

      Observe que MD5 tiene problemas de colisión y debería evitarse en producción.

      A continuación, añadirá un modelo llamado URL con los siguientes campos:

      • full_url: la URL a acortar.
      • url_hash: un hash corto que representa la URL completa.
      • clicks: cuántas veces se accedió a la URL corta.
      • created_at: la fecha y hora en la que se creó la URL.

      shorty/shortener/models.py

      ...
      
      class URL(models.Model):
          full_url = models.URLField(unique=True)
          url_hash = models.URLField(unique=True)
          clicks = models.IntegerField(default=0)
          created_at = models.DateTimeField(auto_now_add=True)
      

      Generará la url_hash aplicando el algoritmo hash MD5 al campo full_url y usando solo los 10 primeros caracteres obtenidos durante el método save() del modelo, que se ejecuta cada vez que Django guarda una entrada en la base de datos. Adicionalmente, los acortadores de URL normalmente realizan un seguimiento de la cantidad de clics que se hacen sobre un enlace. Conseguirá esto invocando el método clicked() cuando un usuario visita una URL.

      Las operaciones mencionadas se añadirán dentro de su modelo URL con este código:

      shorty/shortener/models.py

      ...
      
          def clicked(self):
              self.clicks += 1
              self.save()
      
          def save(self, *args, **kwargs):
              if not self.id:
                  self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
      
              return super().save(*args, **kwargs)
      

      Ahora que ha revisado el código, abra el archivo shortener/models.py:

      Sustituya el código con el siguiente contenido:

      shorty/shortener/models.py

      from hashlib import md5
      
      from django.db import models
      
      
      class URL(models.Model):
          full_url = models.URLField(unique=True)
          url_hash = models.URLField(unique=True)
          clicks = models.IntegerField(default=0)
          created_at = models.DateTimeField(auto_now_add=True)
      
          def clicked(self):
              self.clicks += 1
              self.save()
      
          def save(self, *args, **kwargs):
              if not self.id:
                  self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
      
              return super().save(*args, **kwargs)
      

      Asegúrese de guardar y cerrar el archivo.

      Para aplicar estos cambios en la base de datos, deberá crear las migraciones ejecutando el siguiente comando:

      • python manage.py makemigrations

      Esto generará el siguiente resultado:

      Output

      Migrations for 'shortener': shortener/migrations/0001_initial.py - Create model URL

      Luego ejecute las migraciones:

      Verá el siguiente resultado en su terminal:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, shortener Running migrations: Applying shortener.0001_initial... OK

      Ahora que ha configurado los modelos, en el siguiente paso creará el endpoint GraphQL y una “Query”.

      Paso 3: Crear consultas

      La arquitectura REST expone diferentes recursos en diferentes extremos, cada uno con una estructura de datos bien definida. Por ejemplo, puede recuperar una lista de usuarios en /api/users, esperando siempre los mismos campos. GraphQL, por otro lado, tiene un endpoint único para todas las interacciones, y utiliza Query para acceder a los datos. La principal diferencia, y la más valiosa, es que puede usar una “Query” para recuperar todos los usuarios en una única solicitud.

      Comience creando una “Query” para recuperar todas las URL. Necesitará algunos elementos:

      • Un tipo de URL, vinculado a su modelo definido previamente.
      • Una instrucción “Query” llamada urls.
      • Un método para resolver su “Query”, lo que implica obtener todas las URL de la base de datos y devolverlas al cliente.

      Cree un nuevo archivo llamado shortener/shema.py:

      Comience añadiendo las instrucciones import de Python:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      
      from .models import URL
      

      La primera línea importa la biblioteca principal graphene, que contiene los tipos GraphQL básicos, como List. DjangoObjectType es un elemento auxiliar para crear una definición de esquema desde cualquier modelo de Django y la tercera línea importa su modelo URL creado previamente.

      Tras eso, cree un nuevo tipo de GraphQL para el modelo URL añadiendo las siguientes líneas:

      shorty/shortener/schema.py

      ...
      class URLType(DjangoObjectType):
          class Meta:
              model = URL
      

      Finalmente, añada estas líneas para crear un tipo de “Query” para el modelo URL:

      shorty/shortener/schema.py

      ...
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType)
      
          def resolve_urls(self, info, **kwargs):
              return URL.objects.all()
      

      Este código crea una clase Query con un campo llamado urls, que es una lista del URLType definido previamente. Cuando se resuelve la “Query” a través del método resolve_urls, muestra las URL almacenadas en la base de datos.

      El archivo completo shorterner/schema.py se muestra aquí:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      
      from .models import URL
      
      
      class URLType(DjangoObjectType):
          class Meta:
              model = URL
      
      
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType)
      
          def resolve_urls(self, info, **kwargs):
              return URL.objects.all()
      

      Guarde y cierre el archivo.

      Todas las “Query” deben añadirse ahora al esquema principal. Piense en él como un depósito para todos sus recursos.

      Cree un nuevo archivo en la ruta shorty/schema.py​​​ y ábralo con su editor:

      Importe los siguientes paquetes de Python añadiendo las líneas que se muestran a continuación. La primera, como se ha mencionado, contiene los tipos GraphQL básicos. La segunda línea importa el archivo de esquema creado previamente.

      shorty/shorty/schema.py

      import graphene
      
      import shortener.schema
      

      A continuación, añada la clase Query principal. Contendrá, mediante herencia, todas las “Query” para las operaciones futuras creadas:

      shorty/shorty/schema.py

      ...
      class Query(shortener.schema.Query, graphene.ObjectType):
          pass
      

      Por último, cree la variable schema:

      shorty/shorty/schema.py

      ...
      schema = graphene.Schema(query=Query)
      

      El ajuste SCHEMA que definió en el paso 2 apunta a la variable schema que acaba de crear.

      El archivo completo shorty/schema.py se muestra aquí:

      shorty/shorty/schema.py

      import graphene
      
      import shortener.schema
      
      
      class Query(shortener.schema.Query, graphene.ObjectType):
          pass
      
      schema = graphene.Schema(query=Query)
      

      Guarde y cierre el archivo.

      A continuación, habilite el extremo GraphQL y la interfaz GraphiQL, una interfaz web gráfica que se usa para interactuar con el sistema GraphQL.

      Abra el archivo shorty/urls.py:

      Con fines de aprendizaje, elimine el contenido del archivo y guárdelo, para que pueda comenzar desde cero.

      Las primeras líneas que añadirá son las instrucciones de importación de Python:

      shorty/shorty/urls.py

      from django.urls import path
      from django.views.decorators.csrf import csrf_exempt
      
      from graphene_django.views import GraphQLView
      

      Django utiliza la función path para crear una URL accesible para la interfaz GraphiQL. A continuación, importe csrf_exempt, que permite a los clientes enviar datos al servidor. Puede encontrar una explicación completa en la Documentación de Graphene. En la última línea, importó el código real responsable de la interfaz a través de GraphQLView.

      A continuación, cree una variable llamada urlpatterns.

      shorty/shorty/urls.py

      ...
      urlpatterns = [
          path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
      ]
      

      Esto unirá todo el código necesario para hacer que la interfaz GraphiQL esté disponible en la ruta graphql/:

      El archivo completo shorterner/urls.py se muestra aquí:

      shorty/shorty/urls.py

      from django.urls import path
      from django.views.decorators.csrf import csrf_exempt
      
      from graphene_django.views import GraphQLView
      
      urlpatterns = [
          path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
      ]
      

      Guarde el archivo y ciérrelo.

      De vuelta en el terminal, ejecute el comando python manage.py runserver (si no se está ejecutando aún):

      • python manage.py runserver

      Abra su navegador web en la dirección http://localhost:8000/graphql. Se le presentará esta pantalla:

      Interfaz GraphiQL

      GraphiQL es una interfaz donde puede ejecutar instrucciones de GraphQL y ver los resultados. Una función es la sección Docs en la parte superior derecha. Debido a que en GraphQL se debe escribir todo, obtendrá documentación gratuita sobre sus “Type”(tipo), “Query” (consulta) y “Mutation” (mutación), entre otros aspectos.

      Tras explorar la página, inserte su primera “Query” en el área de texto principal:

      query {
        urls {
          id
          fullUrl
          urlHash
          clicks
          createdAt
        }
      }
      

      Este contenido muestra cómo se estructura una “Query” de GraphQL: primero utiliza la palabra clave query para indicar al servidor que solo desea recuperar algunos de los datos. A continuación, usará el campo urls definido en el archivo shortener/schema.py dentro de la clase Query. Desde ahí, solicita explícitamente todos los campos definidos en el modelo URL usando el estilo de capitalización camel, que es el predeterminado para GraphQL.

      Ahora, haga clic en el botón de flecha de reproducción en la parte superior izquierda.

      Recibirá la siguiente respuesta, indicando que aún no tiene URLs:

      Output

      { "data": { "urls": [] } }

      Esto muestra que GraphQL está funcionando. En su terminal, pulse CTRL+C para detener su servidor.

      En este paso, logró un gran avance al crear el extremo de GraphQL, realizar una “Query” para obtener todas las URLs y habilitar la interfaz de GraphiQL. Ahora, creará “Mutation” para cambiar la base de datos.

      Paso 4: Crear mutaciones

      La mayoría de las aplicaciones tienen una forma de cambiar el estado de la base de datos añadiendo, actualizando o eliminando datos. En GraphQL, estas operaciones se denominan Mutation. Tienen el aspecto de las “Query” pero utilizan argumentos para enviar datos al servidor.

      Para crear su primera “Mutation”, abra shortener/schema.py:

      Al final del archivo, comience añadiendo una nueva clase llamada CreateURL:

      shorty/shortener/schema.py

      ...
      class CreateURL(graphene.Mutation):
          url = graphene.Field(URLType)
      

      Esta clase hereda el elemento auxiliar graphene.Mutation para tener las capacidades de una mutación GraphQL. También tiene un nombre de propiedad url, que define el contenido mostrado por el servidor tras completarse la “Mutation”. En este caso, será una estructura de datos URLType.

      A continuación añada una subclase llamada Arguments a la clase ya definida:

      shorty/shortener/schema.py

      ...
          class Arguments:
              full_url = graphene.String()
      

      Esto define qué datos serán aceptados por el servidor. Aquí está esperando un parámetro llamado full_url con un contenido String:

      Ahora añada las siguientes líneas para crear el método mutate:

      shorty/shortener/schema.py

      ...
      
          def mutate(self, info, full_url):
              url = URL(full_url=full_url)
              url.save()
      

      Este método mutate hace gran parte del trabajo al recibir los datos del cliente y guardarlos en la base de datos. Al final, muestra la clase que contiene el elemento recién creado.

      Por último, cree una clase Mutation para albertar todas las “Mutation” para su app añadiendo estas líneas:

      shorty/shortener/schema.py

      ...
      
      class Mutation(graphene.ObjectType):
          create_url = CreateURL.Field()
      

      Hasta ahora, solo tendrá una mutación llamada create_url.

      El archivo completo shorterner/schema.py se muestra aquí:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      
      from .models import URL
      
      
      class URLType(DjangoObjectType):
          class Meta:
              model = URL
      
      
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType)
      
          def resolve_urls(self, info, **kwargs):
              return URL.objects.all()
      
      
      class CreateURL(graphene.Mutation):
          url = graphene.Field(URLType)
      
          class Arguments:
              full_url = graphene.String()
      
          def mutate(self, info, full_url):
              url = URL(full_url=full_url)
              url.save()
      
              return CreateURL(url=url)
      
      
      class Mutation(graphene.ObjectType):
          create_url = CreateURL.Field()
      

      Cierre y guarde el archivo.

      Para terminar de añadir la “Mutation”, cambie el archivo shorty/schema.py:

      Altere el archivo para incluya el siguiente código resaltado:

      shorty/shorty/schema.py

      
      import graphene
      
      import shortener.schema
      
      
      class Query(shortener.schema.Query, graphene.ObjectType):
          pass
      
      
      class Mutation(shortener.schema.Mutation, graphene.ObjectType):
          pass
      
      
      schema = graphene.Schema(query=Query, mutation=Mutation)
      

      Guarde y cierre el archivo. Si no está ejecutando el servidor local, inícielo:

      • python manage.py runserver

      Navegue a http://localhost:8000/graphql en su navegador web. Ejecute su primera “Mutation” en la interfaz web GrapiQL ejecutando la siguiente instrucción:

      mutation {
        createUrl(fullUrl:"https://www.digitalocean.com/community") {
          url {
            id
            fullUrl
            urlHash
            clicks
            createdAt
          }
        }
      }
      

      Compuso la “Mutation” con el nombre createURL, el argumento fullUrl y los datos que desea en la respuesta definida dentro del campo url.

      El resultado contendrá la información de la URL que acaba de crear dentro del campo de GraphQL data, como se muestra aquí:

      Output

      { "data": { "createUrl": { "url": { "id": "1", "fullUrl": "https://www.digitalocean.com/community", "urlHash": "077880af78", "clicks": 0, "createdAt": "2020-01-30T19:15:10.820062+00:00" } } } }

      Con eso, se añadió una URL a la base de datos con su versión en hash, como puede ver en el campo urlHash. Intente ejecutar la “Query” que creó en el último paso para ver su resultado:

      query {
        urls {
          id
          fullUrl
          urlHash
          clicks
          createdAt
        }
      }
      

      El resultado mostrará la URL almacenada:

      Output

      { "data": { "urls": [ { "id": "1", "fullUrl": "https://www.digitalocean.com/community", "urlHash": "077880af78", "clicks": 0, "createdAt": "2020-03-18T21:03:24.664934+00:00" } ] } }

      También puede intentar ejecutar la misma “Query”, pero solo pidiendo los campos que desea.

      A continuación, inténtelo una vez más con una URL diferente:

      mutation {
        createUrl(fullUrl:"https://www.digitalocean.com/write-for-donations/") {
          url {
            id
            fullUrl
            urlHash
            clicks
            createdAt
          }
        }
      }
      

      El resultado será lo siguiente:

      Output

      { "data": { "createUrl": { "url": { "id": "2", "fullUrl": "https://www.digitalocean.com/write-for-donations/", "urlHash": "703562669b", "clicks": 0, "createdAt": "2020-01-30T19:31:10.820062+00:00" } } } }

      El sistema ahora puede crear URLs cortas y listarlas. En el siguiente paso, permitirá a los usuarios acceder a una URL por medio de su versión corta y los redirigirá a la página correcta.

      Paso 5: Crear el endpoint de acceso

      En este paso, usará el método Django Views, un método que toma una solicitud y devuelve una respuesta, para redirigir a cualquiera que acceda al endpoint http://localhost:8000/url_hash a su URL completa.

      Abra el archivo shortener/views.py con su editor:

      Para comenzar, importe los dos paquetes sustituyendo el contenido con las siguientes líneas:

      shorty/shortener/views.py

      from django.shortcuts import get_object_or_404, redirect
      
      from .models import URL
      

      Estas se explicarán más detenidamente más adelante.

      A continuación, creará un Django View llamado root. Añada este snippet de código responsable de View al final del archivo:

      shorty/shortener/views.py

      ...
      
      def root(request, url_hash):
          url = get_object_or_404(URL, url_hash=url_hash)
          url.clicked()
      
          return redirect(url.full_url)
      

      Esto recibe un argumento llamado url_hash desde la URL solicitada por un usuario. Dentro de la función, la primera línea intenta obtener la URL de la base de datos usando el argumento url_hash. Si no se encuentra, devuelve el error HTTP 404 al cliente, lo que significa que falta el recurso. Después, aumenta la propiedad clicked de la entrada de URL, asegurando que realiza un seguimiento de la cantidad de veces que se accede a la URL. Al final, redirige el cliente a la URL solicitada.

      El archivo completo shorterner/views.py se muestra aquí:

      shorty/shortener/views.py

      from django.shortcuts import get_object_or_404, redirect
      
      from .models import URL
      
      
      def root(request, url_hash):
          url = get_object_or_404(URL, url_hash=url_hash)
          url.clicked()
      
          return redirect(url.full_url)
      

      Guarde y cierre el archivo.

      A continuación, abra shorty/urls.py:

      Añada el siguiente código resaltado para habilitar la vista root.

      shorty/shorty/urls.py

      
      from django.urls import path
      from django.views.decorators.csrf import csrf_exempt
      
      from graphene_django.views import GraphQLView
      
      from shortener.views import root
      
      
      urlpatterns = [
          path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
          path('<str:url_hash>/', root, name='root'),
      ]
      

      La vista root será accesible en la ruta / de su servidor, aceptando un url_hash como parámetro de la cadena.

      Guarde y cierre el archivo. Si no está ejecutando el servidor local, inícielo ejecutando el comando python manage.py runserver.

      Para probar su nueva adición, abra su navegador web y acceda a la URL http://localhost:8000/077880af78. Observe que la última parte de la URL es el hash creado por la “Mutation” del paso 5. Accederá a la página URL del hash; en este caso, el sitio web de la comunidad de DigitalOcean.

      Ahora que el redireccionamiento de la URL funciona, hará que la aplicación sea más segura implementando la gestión de errores cuando se ejecute la “Mutation”.

      Paso 6: Implementar la gestión de errores

      Gestionar errores es una práctica recomendada en todas las aplicaciones, ya que los desarrolladores normalmente no controlan lo que se enviará al servidor. En este caso, puede intentar prever los fallos y minimizar sus impactos. En un sistema complejo como GraphQL, pueden salir mal muchas cosas, desde que el cliente pida los datos erróneos erróneos hasta que el servidor pierda el acceso a la base de datos.

      Como sistema escrito, GraphQL puede verificar todo lo que el cliente solicite y reciba en una operación conocida como validación de esquema. Puede ver esto en acción realizando una “Query” con un campo no existente.

      Diríjase a http://localhost:8000/graphql​​​ en su navegador una vez más y ejecute la siguiente “Query” en la interfaz GraphiQL con el campo iDontExist:

      query {
        urls {
          id
          fullUrl
          urlHash
          clicks
          createdAt
          iDontExist
        }
      }
      

      Ya que no hay un campo iDontExist definido en su “Query”, GraphQL muestra un mensaje de error:

      Output

      { "errors": [ { "message": "Cannot query field "iDontExist" on type "URLType".", "locations": [ { "line": 8, "column": 5 } ] } ] }

      Esto es importante porque, en el sistema escrito de GraphQL, el objetivo es enviar y recibir solo la información ya definida en el esquema.

      La aplicación actual acepta cualquier cadena arbitraria en el campo full_url. El problema es que si alguien envía una URL mal construida, usted no redirigiría al usuario a ningún sitio al probar la información almacenada. En este caso, necesita verificar si la full_url está bien formateada antes de guardarla en la base de datos, y, si hay cualquier error, elevar la excepción GraphQLError con un mensaje personalizado.

      Vamos a implementar esta funcionalidad en dos pasos. Primero, abra el archivo shortener/models.py:

      Añada las líneas resaltadas en la sección import:

      shorty/shortener/models.py

      from hashlib import md5
      
      from django.db import models
      from django.core.validators import URLValidator
      from django.core.exceptions import ValidationError
      
      from graphql import GraphQLError
      ...
      

      El URLValidator es un ayudante de Django para validar una cadena URL y Graphene utiliza GraphQLError para elevar excepciones con un mensaje personalizado.

      A continuación, asegúrese de validar la URL recibida por el usuario antes de guardarla en la base de datos. Habilite esta operación añadiendo el código resaltado en el archivo shortener/models.py:

      shorty/shortener/models.py

      class URL(models.Model):
          full_url = models.URLField(unique=True)
          url_hash = models.URLField(unique=True)
          clicks = models.IntegerField(default=0)
          created_at = models.DateTimeField(auto_now_add=True)
      
          def clicked(self):
              self.clicks += 1
              self.save()
      
          def save(self, *args, **kwargs):
              if not self.id:
                  self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
      
              validate = URLValidator()
              try:
                  validate(self.full_url)
              except ValidationError as e:
                  raise GraphQLError('invalid url')
      
              return super().save(*args, **kwargs)
      

      Primero, este código inicia el URLValidator en la variable validate. Dentro del bloque try/except, validate() la URL recibida y eleve un GraphQLError con el mensaje personalizado invalid url si se produjo algún error.

      El archivo completo shorterner/models.py se muestra aquí:

      shorty/shortener/models.py

      from hashlib import md5
      
      from django.db import models
      from django.core.validators import URLValidator
      from django.core.exceptions import ValidationError
      
      from graphql import GraphQLError
      
      
      class URL(models.Model):
          full_url = models.URLField(unique=True)
          url_hash = models.URLField(unique=True)
          clicks = models.IntegerField(default=0)
          created_at = models.DateTimeField(auto_now_add=True)
      
          def clicked(self):
              self.clicks += 1
              self.save()
      
          def save(self, *args, **kwargs):
              if not self.id:
                  self.url_hash = md5(self.full_url.encode()).hexdigest()[:10]
      
              validate = URLValidator()
              try:
                  validate(self.full_url)
              except ValidationError as e:
                  raise GraphQLError('invalid url')
      
              return super().save(*args, **kwargs)
      

      Guarde y cierre el archivo. Si no está ejecutando el servidor local, inícielo con el comando python manage.py runserver.

      A continuación, pruebe su nueva gestión de errores en http://localhost:8000/graphql. Intente crear una nueva URL con una full_url no válida en la interfaz GraphiQL:

      mutation {
        createUrl(fullUrl:"not_valid_url"){
          url {
            id
            fullUrl
            urlHash
            clicks
            createdAt
          }
        }
      }
      

      Cuando envíe una URL no válida, su excepción se elevará con el mensaje personalizado:

      Output

      { "errors": [ { "message": "invalid url", "locations": [ { "line": 2, "column": 3 } ], "path": [ "createUrl" ] } ], "data": { "createUrl": null } }

      Si mira en su terminal donde está en ejecución el comando python manage.py runserver, aparecerá un error:

      Output

      ... graphql.error.located_error.GraphQLLocatedError: invalid url [30/Jan/2020 19:46:32] "POST /graphql/ HTTP/1.1" 200 121

      Un extremo de GraphQL siempre generará un error con un código de estado HTTP 200, lo que normalmente significa que el resultado es correcto. Recuerde que, aunque GraphQL se construye sobre HTTP, no utiliza los conceptos de los códigos de estado HTTP o los métodos HTTP como REST hace.

      Con el manejo de errores implementado, ahora puede implementar un mecanismo para filtrar sus “Query”, minimizando la información devuelta por el servidor.

      Paso 7: Implementar filtros

      Imagine que ha comenzado a usar el acortador de URL para añadir sus propios enlaces. Después de un tiempo, habrá tantas entradas que será difícil encontrar la correcta. Puede resolver este problema usando filtros.

      El filtrado es un concepto común en las API de REST; normalmente, se anexa a la URL un parámetro Query con un campo y un valor. A modo de ejemplo, para filtrar todos los usuarios llamados jojo, podría usar GET /api/users?name=jojo.

      En GraphQL, usará argumentos “Query” como filtros. Crean una interfaz perfecta y limpia.

      Puede resolver el problema “hard to find a URL” permitiendo que el cliente filtre las URL por nombre con el campo full_url. Para implementar eso, abra el archivo shortener/schema.py en su editor favorito.

      Primero, importe el método Q en la línea resaltada:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      from django.db.models import Q
      
      from .models import URL
      ...
      

      Esto se usará para filtrar la consulta de su base de datos.

      A continuación, reescriba toda la clase Query con el siguiente contenido:

      shorty/shortener/schema.py

      ...
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType, url=graphene.String())
      
          def resolve_urls(self, info, url=None, **kwargs):
              queryset = URL.objects.all()
      
              if url:
                  _filter = Q(full_url__icontains=url)
                  queryset = queryset.filter(_filter)
      
              return queryset
      ...
      

      Las modificaciones que está realizando son:

      • Añadir el parámetro de filtro url dentro de la variable urls y el método resolve_url
      • Dentro de resolve_urls, si se da un parámetro llamado url, filtrar los resultados de la base de datos para devolver solo las URLs que contienen el valor dado, usando el método Q(full_url__icontains=url).

      El archivo completo shorterner/schema.py se muestra aquí:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      from django.db.models import Q
      
      from .models import URL
      
      
      class URLType(DjangoObjectType):
          class Meta:
              model = URL
      
      
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType, url=graphene.String())
      
          def resolve_urls(self, info, url=None, **kwargs):
              queryset = URL.objects.all()
      
              if url:
                  _filter = Q(full_url__icontains=url)
                  queryset = queryset.filter(_filter)
      
              return queryset
      
      
      class CreateURL(graphene.Mutation):
          url = graphene.Field(URLType)
      
          class Arguments:
              full_url = graphene.String()
      
          def mutate(self, info, full_url)
              url = URL(full_url=full_url)
              url.save()
      
              return CreateURL(url=url)
      
      
      class Mutation(graphene.ObjectType):
          create_url = CreateURL.Field()
      

      Guarde y cierre el archivo. Si no ejecuta el servidor local, inícielo con python manage.py runserver.

      Pruebe sus últimos cambios en http://localhost:8000/graphql. En la interfaz GraphiQL, escriba la siguiente instrucción. Filtrará todas las URL con la palabra community:

      query {
        urls(url:"community") {
          id
          fullUrl
          urlHash
          clicks
          createdAt
        }
      }
      

      El resultado es solo una entrada, ya que acaba de añadir una URL con la cadena community en ella. Si añadió más URL antes, el resultado puede variar.

      Output

      { "data": { "urls": [ { "id": "1", "fullUrl": "https://www.digitalocean.com/community", "urlHash": "077880af78", "clicks": 1, "createdAt": "2020-01-30T19:27:36.243900+00:00" } ] } }

      Ahora, tiene la capacidad de buscar en sus URLs. Sin embargo, con demasiados enlaces, sus clientes pueden quejarse de que la lista de URLs devuelve más datos de los que la aplicación puede gestionar. Para resolver esto, implementará la paginación.

      Paso 8 – Implementar la paginación

      Los clientes que usen su backend pueden quejarse de que el tiempo de respuesta es demasiado largo o que el tamaño es demasiado grande si hay demasiadas entradas de URL. Incluso puede ser difícil para su base de datos reunir un conjunto tan enorme de información. Para resolver este problema, puede permitir que el cliente especifique cuántos elementos quiere dentro de cada solicitud usando una técnica llamada paginación.

      No existe una forma predeterminada de implementar esta función. Incluso en las API de REST puede verlo en los encabezados HTTP o en los parámetros de consulta, con diferentes nombres y comportamientos.

      En esta aplicación, implementará la paginación habilitando dos argumentos más en la “Query” de URL: first y skip first seleccionará el primer número de elementos variables y skip especificará cuántos elementos deben omitirse desde el principio. Por ejemplo, si usa first == 10 y skip == 5 se obtendrán las primeras 10 URL, pero se omitirán 5, con lo cual se mostrarán solo las 5 restantes.

      Implementar esta solución es similar a añadir un filtro.

      Abra el archivo shortener/schema.py:

      En el archivo, cambie la clase Query añadiendo los dos nuevos parámetros en la variable urls y el método resolve_urls, resaltados en el siguiente código:

      shorty/shortener/schema.py

      import graphene
      from graphene_django import DjangoObjectType
      from django.db.models import Q
      
      from .models import URL
      
      
      class Query(graphene.ObjectType):
          urls = graphene.List(URLType, url=graphene.String(), first=graphene.Int(), skip=graphene.Int())
      
          def resolve_urls(self, info, url=None, first=None, skip=None, **kwargs):
              queryset = URL.objects.all()
      
              if url:
                  _filter = Q(full_url__icontains=url)
                  queryset = queryset.filter(_filter)
      
              if first:
                  queryset = queryset[:first]
      
              if skip:
                  queryset = queryset[skip:]
      
              return queryset
      ...
      

      Este código utiliza los parámetros first y skip recién creados dentro del método resolve_urls para filtrar la consulta de la base de datos.

      Guarde y cierre el archivo. Si no está ejecutando el servidor local, inícielo con python manage.py runserver.

      Para probar la paginación, emita la siguiente “Query” en la interfaz GraphiQL en http://localhost:8000/graphql:

      query {
        urls(first: 2, skip: 1) {
          id
          fullUrl
          urlHash
          clicks
          createdAt
        }
      }
      

      Su acortador de URL mostrará la segunda URL creada en su base de datos:

      Output

      { "data": { "urls": [ { "id": "2", "fullUrl": "https://www.digitalocean.com/write-for-donations/", "urlHash": "703562669b", "clicks": 0, "createdAt": "2020-01-30T19:31:10.820062+00:00" } ] } }

      Esto muestra que la función de paginación funciona. Puede experimentar añadiendo más URLs y probando diferentes conjuntos de first y skip.

      Conclusión

      Todo el ecosistema de GraphQL crece día a día y una comunidad activa lo respalda. Empresas como GitHub y Facebook han demostrado que está listo para la producción, y ahora puede aplicar esta tecnología a sus propios proyectos.

      En este tutorial, creó un acortador de URL usando GraphQL, Python y Django y conceptos como “Query” y “Mutation”. Pero sobre todo, ahora comprende cómo depender de estas tecnologías para crear aplicaciones web usando el marco web Django.

      Puede explorar más sobre GraphQL y las herramientas usadas aquí en el sitio web de GraphQL y en los sitios web de documentación de Graphene. Además, DigitalOcean tiene tutoriales adicionales para Python y Django que puede usar si desea aprender más sobre esto.



      Source link