One place for hosting & domains

      Platform

      Getting Started on the Linode Platform


      Thank you for choosing Linode as your cloud hosting provider! This guide will help you sign up for an account and access Linode’s
      Cloud Manager
      , a browser-based control panel which allows you to manage your Linode virtual servers and services.

      1. Sign Up for an Account
      2. Navigate the Cloud Manager
      3. Create Your First Compute Instance
      4. Create Additional Services
      5. Understand Billing
      6. Explore Linode Guides

      Sign Up for an Account

      First, you need to create a Linode account to start using our services. If you already have a Linode account, you can skip to the next section.

      1. Click the Sign Up button at the top of this page.

        Note

        Sign up today
        and receive a $100 credit. This credit will be applied to any valid services used during your first 60 days.
      2. Enter your email address, a unique username, and a strong password. Alternatively, you can sign up using your existing Google or GitHub account.

      3. A confirmation email will be sent to your email address. Click the link to confirm your email and be taken to the next step.

      4. Enter your billing and payment information. A valid credit or debit card must be used when creating an account, though additional payment methods (like Google Pay and PayPal) are available once the account is active.

      Most accounts are activated instantly, but some require manual review prior to activation.

      Note

      Sending email: Newly created Linode accounts have restrictions on ports 25, 465, and 587 applied to Compute Instances, which prevent instances from sending email. If you’d like to send email on a Compute Instance, review the
      Running a Mail Server
      guide, then
      open a ticket
      with our Support team.

      Once your account has been created, consider enabling 2FA to prevent unauthorized access. Anyone that has access to your account may be able to delete your services, add new ones, and reset the root password on your Compute Instances to gain access to them. See
      Cloud Manager Security Controls
      for instructions on enabling 2FA. If you are logging in with a third party provider, consider adding 2FA through that provider.

      Linode’s
      Cloud Manager
      is the primary gateway to our platform. It enables you to manage your account, view your bills, add services, and much more. Here are some of the main sections you might want to look through:

      • Linodes
        : View a list of your Compute Instances. Clicking on one takes you to its details page, where you can power it on or off, reboot it, resize it, access the console, change its configuration, attach Block Storage volumes, and lots more.

      • Account
        : View your account, billing information, payment methods, and previous invoices. You can also add additional users to your account.

      • Help & Support
        : Search through our guides, find answers on the Community Site, or open a ticket to reach our 24/7 award-winning
        Support Team
        .

      For a full overview of the Cloud Manager and its core features, see the
      Overview of the Cloud Manager
      guide.

      Create Your First Compute Instance

      Compute Instances (also frequently called Linodes) are virtual machines that can be used for nearly any purpose. Many of our customers use them to host websites and web applications, but they can also be used for development workloads, game servers, big data processing, machine learning, scientific processing, and much more. They come in a few different types, including
      Shared CPU
      ,
      Dedicated CPU
      ,
      High Memory
      , and
      GPU
      . Each of these are tailored for certain workloads. See the guides below to learn more about creating a Compute Instance, configuring it after deployment, and securing it.

      Create Additional Services

      In addition to
      Compute Instances
      , Linode offers many other services that empower you to get the most out of cloud computing. Take a look through the list below to determine if any of them might be useful to you.

      Compute

      • LKE
        (Linode Kubernetes Engine): Managed Kubernetes clusters that simplify container orchestration.
      • Bare Metal
        (Coming soon): Dedicated single-tenant hardware for advanced workloads.

      Databases

      • Managed Databases
        : Reliable, performant, highly available, and fully managed database clusters to support production database workloads.

      Storage

      • Block Storage
        : Scalable, high-speed, fault-tolerant, and portable (detachable) storage volumes used to add additional storage to a Compute Instance.
      • Object Storage
        : S3-compatible object storage service that can be used independently of a Compute Instance.
      • Backups
        : Fully managed automatic daily, weekly, and biweekly backups of your Linode Compute Instances.
      • Images
        : Create preconfigured disk images (or upload image files) that can be rapidly deployed to new or existing Compute Instances.

      Networking

      • NodeBalancers
        : Managed cloud-based load balancing service used with Compute Instances to enable high availability and horizontal scaling.
      • Cloud Firewalls
        : A free cloud-based firewall service that can be used to secure any Compute Instance.
      • DNS Manager
        : A free comprehensive domain and DNS management service for Linode customers.
      • VLANs
        : Private L2 networks to secure traffic between Compute Instances.

      Understand Billing

      Linode services are billed to your primary payment method at the end of each month (or when your accrued charges reach your billing limit). This means you can continuously add, modify, and remove services throughout the month and, after the month is over, you receive an invoice for the hourly usage of each service up to the monthly cap. In affect, billing is post-paid and pro-rated. See the below resources for more billing information and detailed hourly and monthly pricing.

      Keep in mind that charges will accrue for any active service, even if it is powered off or otherwise not in use. This includes Compute Instances that have been powered off as well as any service you might have added to the account but are not using.

      Explore Linode Guides

      Linode offers extensive documentation. This not only includes guides for our core products and services, but general guides on Linux, the command-line, networking, security, version control, databases, and much more. Learn how to deploy a WordPress One-Click App, run a VPN, install a file storage platform, or even host a website using Object Storage. Whatever cloud computing use cases you have in mind, you can likely do it on Linode and our guides are a great place to start.



      Source link

      From 0 to 3 Million+ Deployments: Scaling App Platform on Kubernetes


      Video

      About the Talk

      Get a better understanding of how your builds run on DigitalOcean App Platform, and how to scale efficiently when your user base expands.

      App Platform, a new DigitalOcean managed-service offering, was released in October 2020 and has already been deployed over 3 million times. This immense growth didn’t come without its challenges around infrastructure, observability, and release velocity.

      Kamal and Nick share techniques and strategies that have helped DigitalOcean overcome these problem areas and the benefits received from each solution.

      About the Presenters

      Kamal Nasser
      Kamal Nasser is a Senior Software Engineer at DigitalOcean. When not automating and playing with modern software and technologies, you’ll likely find him penning early 17th century calligraphy.

      Nick Tate
      Nicholas Tate is a Tech Lead on the App Platform team at DigitalOcean. He has worked on the DigitalOcean Managed Kubernetes team and before that, on multi-cloud Kubernetes at Containership. He loves everything and anything related to cloud native infrastructure and enjoys playing guitar.



      Source link

      How To Build a Python REST API with Fauna and Deploy it to DigitalOcean App Platform


      Introduction

      Many developers don’t have the time or experience to set up and manage infrastructure for their applications. To keep up with deadlines and reduce costs, developers need to find solutions that allow them to deploy their apps to the cloud as quickly and efficiently as possible to focus on writing the code and delivering new features to their customers. Together, DigitalOcean’s App Platform and Fauna provide that ability.

      DigitalOcean App Platform is a Platform-as-a-Service (PaaS) that abstracts the infrastructure that runs your apps. It also lets you deploy applications by pushing your code to a Git branch.

      Fauna is a powerful data layer for applications of any size. As you’ll see in this tutorial, with Fauna, you can get a database up and running quickly without having to worry about the database operations.

      Together, these two solutions let you focus on your application instead of managing your infrastructure.

      In this tutorial, you’ll integrate Fauna with Python by writing a minimal REST API using the Flask framework. You’ll then deploy the API to DigitalOcean’s App Platform from a Git repository.The API will consist of:

      • A public /signup POST endpoint for creating users in the Users collection.
      • A public /login POST endpoint for authenticating with the documents in the Users collection.
      • A private /things GET endpoint for fetching a list of Fauna documents from the Things collection.

      The finished Python project is available at this Github repository.

      Prerequisites

      Before starting this tutorial, you will need:

      Step 1 — Setting Up the Fauna Database

      In the first step, you will configure a Fauna database and create the collections for the API. Fauna is a document-based database rather than a traditional table-based relational database. Fauna stores your data in documents and collections, which are groups of documents.

      To create the collections, you will execute queries using FQL, Fauna’s native query language. FQL is an expressive and powerful query language that gives you access to the full power of Fauna.

      To get started, log into Fauna’s dashboard. After logging in, click on the Create Database button at the top.

      In the New Database form, use PYTHON_API for the database name:

      Creating a Fauna database

      Leave Pre-populate with demo data unchecked. Press the Save button.

      After creating the database, you will see the home section for your database:

      Fauna database home

      You’re now going to create two collections:

      • The Users collection that will store documents with authentication information.
      • The Things collection to store some mock data to test your API.

      To create these collections, you’ll execute some FQL queries in the dashboard’s shell. Access the shell from the main dashboard menu on the left:

      Fauna dashboard shell

      Write the following FQL query in the bottom panel of the shell to create a collection called Things by using the CreateCollection function:

      CreateCollection({name: "Things"})
      

      Press the RUN QUERY button. You will get a result similar to this in the shell’s top panel:

      {
        ref: Collection("Things"),
        ts: 1614805457170000,
        history_days: 30,
        name: "Things"
      }
      

      The result shows four fields:

      • ref is a reference to the collection itself.
      • ts is the timestamp of its creation in microseconds.
      • history_days is how long Fauna will retain changes on documents’ changes.
      • name is the collection name.

      Next, create the Users collection with the following query:

      CreateCollection({name: "Users"})
      

      Now that both collections are in place, you will create your first document.

      Documents in Fauna are somewhat similar to JSON objects. Documents can store strings, numbers, and arrays, but they can also use Fauna data types. A common Fauna type is Ref, which represents a reference to a document in a collection.

      The Create function creates a new document into the specified collection. Run the following query to create a document in the Things collection with two fields:

      Create(
        Collection("Things"),
        {
          data: {
            name: "Banana",
            color: "Yellow"
          }
        }
      )
      

      After running that query, Fauna will return the created document:

      {
        ref: Ref(Collection("Things"), "292079274901373446"),
        ts: 1614807352895000,
        data: {
          name: "Banana",
          color: "Yellow"
        }
      }
      

      The result shows the following fields:

      • ref of type Ref is a reference to this document in the Things collection with the ID 292079274901373446. Do note that your document will have a different ID.
      • ts is the timestamp of its creation in microseconds.
      • data is the actual content of the document.

      This result looks similar to the result you got when you created a collection. That’s because all entities in Fauna (collections, indexes, roles, etc) are actually stored as documents.

      To read documents, use the Get function which accepts a reference of a document. Run the Get query using the reference for your document:

      Get(Ref(Collection("Things"), "292079274901373446"))
      

      The result is the full document:

      {
        ref: Ref(Collection("Things"), "292079274901373446"),
        ts: 1614807352895000,
        data: {
          name: "Banana",
          color: "Yellow"
        }
      }
      

      To get all references for documents stored in a collection, use the Documents function with the Paginate function:

      Paginate(Documents(Collection("Things")))
      

      This query returns a page with an array of references:

      {
        data: [Ref(Collection("Things"), "292079274901373446")]
      }
      

      To get actual documents instead of references, iterate over the references using Map. Then use a Lambda (an anonymous function) to iterate over the array of references and Get each reference:

      Map(
        Paginate(Documents(Collection("Things"))),
        Lambda("ref", Get(Var("ref")))
      )
      

      The result is an array containing full documents:

      {
        data: [
          {
            ref: Ref(Collection("Things"), "292079274901373446"),
            ts: 1614807352895000,
            data: {
              name: "Banana",
              color: "Yellow"
            }
          }
        ]
      }
      

      You’re now going to create the Users_by_username index. You typically use indexes in Fauna to catalog, filter, and sort data, but you can also use them for other purposes like enforcing unique constraints.

      The Users_by_username index will find users by their username, and also enforce a unique constraint to prevent two documents from having the same username.

      Execute this code in the shell to create the index:

      CreateIndex({
        name: "Users_by_username",
        source: Collection("Users"),
        terms: [{ field: ["data", "username"] }],
        unique: true
      })
      

      The CreateIndex function will create an index with the configured settings:

      • name is the name of the index.
      • source is the collection (or collections) the index will index data from.
      • terms is the search/filter terms you’ll pass to this index when using it to find documents.
      • unique means that the indexed values will be unique. In this example, the username property of the documents in the Users collection will be enforced as unique.

      To test the index, create a new document inside the Users collection by running the following code in the Fauna shell:

      Create(
        Collection("Users"),
        {
          data: {
            username: "sammy"
          }
        }
      )
      

      You’ll see a result like the following:

      {
        ref: Ref(Collection("Users"), "292085174927098368"),
        ts: 1614812979580000,
        data: {
          username: "sammy"
        }
      }
      

      Now try to create a document with the same username value:

      Create(
        Collection("Users"),
        {
          data: {
            username: "sammy"
          }
        }
      )
      

      You’ll receive an error now:

      Error: [
        {
          "position": [
            "create"
          ],
          "code": "instance not unique",
          "description": "document is not unique."
        }
      ]
      

      Now that the index is in place, you can query it and fetch a single document. Run this code in the shell to fetch the sammy user using the index:

      Get(
        Match(
          Index("Users_by_username"),
          "sammy"
        )
      )
      

      Here’s how it works:

      • Index returns a reference to the Users_by_username index.
      • Match returns a reference to the matched document (the one that has a username with the value of sammy).
      • Get takes the reference returned by Match, and fetches the actual document.

      The result of this query will be:

      {
        ref: Ref(Collection("Users"), "292085174927098368"),
        ts: 1614812979580000,
        data: {
          username: "sammy"
        }
      }
      

      Delete this testing document by passing its reference to the Delete function:

      Delete(Ref(Collection("Users"), "292085174927098368"))
      

      Next you’ll configure security settings for Fauna so you can connect to it from your code.

      Step 2 — Configuring a Server Key and Authorization Rules

      In this step you’ll create a server key that your Python application will use to communicate with Fauna. Then you’ll configure access permissions.

      To create a key, go to the Security section of the Fauna dashboard by using the main menu on the left. Once there:

      1. Press the New Key button.
      2. Select the Server role.
      3. Press Save.

      Creating a Fauna key

      After saving, the dashboard will show you the key’s secret. Save the secret somewhere safe and never commit it to your Git repository.

      Warning: The Server role is omnipotent and anyone with this secret would have full access to your database. As its name implies, this is the role typically used by trusted server applications, although it is also possible to create a key with a custom role with limited privileges. When you create production applications, you’ll want to make a more restrictive role.

      By default, everything in Fauna is private, so you’re now going to create a new role to allow the logged-in users to read documents from the Things collection.

      In the Security section of the dashboard, go to Roles, and create a new custom role with the name User.

      In the Collections dropdown, add the Things collection and press the Read permission so that it shows a green check mark:

      Configuring the permissions of a Fauna role

      Before saving the role, go to the Membership tab and add the Users collection to the role:

      Configuring the memerbship of a Fauna role

      You can now save your User custom role by pressingthe Save button.

      Now any logged-in user from a document in the Users collection will be able to read any document from the Things collection.

      With authentication and authorization in place, let’s now create the Python API that will talk to Fauna.

      Step 3 — Building the Python Application

      In this step you will build a small REST API using the Flask framework, and you’ll write FQL queries in Python, connecting to your Fauna database using the the Fauna driver.

      To get started, create a project folder and access it from your terminal.

      First install Flask with:

      Then install the Fauna Python driver with:

      In your project folder, create the file main.py and add the following code to the file, which adds the necessary imports, the FAUNA_SECRET environment variable, and the basic configuration of the Flask application:

      main.py

      import os
      FAUNA_SECRET = os.environ.get('FAUNA_SECRET')
      
      import flask
      from flask import request
      
      import faunadb
      from faunadb import query as q
      from faunadb.client import FaunaClient
      
      app = flask.Flask(__name__)
      app.config["DEBUG"] = True
      

      The FAUNA_SECRET environment variable will carry the server secret you created earlier. To be able to run this application, locally or in the cloud, this variable needs to be injected. Without it, the application won’t be able to connect to Fauna. You’ll provide this environment variable when you launch the app.

      Now add the the /signup route to the main.py file. This will create new documents in the Users collection:

      main.py

      @app.route('/signup', methods=['POST'])
      def signup():
      
          body = request.json
          client = FaunaClient(secret=FAUNA_SECRET)
      
          try:
              result = client.query(
                  q.create(
                      q.collection("Users"),
                      {
                          "data": {
                              "username": body["username"]
                          },
                          "credentials": {
                              "password": body["password"]
                          }
                      }
                  )
              )
      
              return {
                  "userId": result['ref'].id()
              }
      
          except faunadb.errors.BadRequest as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 409
      

      Note that the Fauna client is being instantiated on every request using the server secret:

      main.py

      ...
      client = FaunaClient(secret=FAUNA_SECRET)
      ...
      

      Once users are logged in, the API will execute queries on behalf of each user using different secrets, which is why it makes sense to instantiate the client on every request.

      Unlike other databases, the Fauna client does not maintain a persistent connection. From the outside world, Fauna behaves like an API; every query is a single HTTP request.

      After the client is ready, the FQL query executes, which creates a new document in the Users collection. Each Fauna driver translates idiomatic syntax to FQL statements. In this route, you added this query:

      main.py

      ...
      q.create(
          q.collection("Users"),
          {
              "data": {
                  "user": json["user"]
              },
              "credentials": {
                  "password": json["password"]
              }
          }
      )
      ...
      

      This is what this query would look like in native FQL:

      Create(
          Collection("Users"),
          {
              "data": {
                  "user": "sammy"
              },
              "credentials": {
                  "password": "secretpassword"
              }
          }
      )
      

      In addition to the document data, you’re adding a credentials configuration with the user’s password. This part of the document is completely private. You will never be able to read a document’s credentials afterwards. When using Fauna’s authentication system, it’s not possible to expose users’ passwords by mistake.

      Finally, if there’s already a user with the same username, a faunadb.errors.BadRequest exception will be raised, and a 409 response with the error information will be returned to the client.

      Next, add the /login route in the main.py file to authenticate the user and password. This follows a similar pattern as the previous example; you execute a query using the Fauna connection and if the authentication fails, you raise a faunadb.errors.BadRequest exception and return a a 401 response with the error information. Add this code to main.py:

      main.py

      @app.route('/login', methods=['POST'])
      def login():
      
          body = request.json
          client = FaunaClient(secret=FAUNA_SECRET)
      
          try:
              result = client.query(
                  q.login(
                      q.match(
                          q.index("Users_by_username"),
                          body["username"]
                      ),
                      {"password": body["password"]}
                  )
              )
      
              return {
                  "secret": result['secret']
              }
      
          except faunadb.errors.BadRequest as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 401
      

      This is the FQL query used to authenticate users with Fauna:

      main.py

      q.login(
          q.match(
              q.index("Users_by_username"),
              body["username"]
          ),
          {"password": body["password"]}
      )
      

      This is what this query would look like in native FQL:

      Login(
          Match(
              Index("Users_by_username"),
              "sammy"
          ),
          {"password": "secretpassword"}
      )
      

      Match returns a reference to a document using the Users_by_username index that we created previously.

      If the provided password matches the referenced document, Login will create a new token and return a dictionary with the following keys:

      • ref with a reference to the token for the new document.
      • ts with the timestamp of the transaction.
      • instance with a reference to the document that was used to do the authentication.
      • secret with the token’s secret that will be used to make further queries to Fauna.

      If you run that FQL query into your Fauna dashboard’s shell you will see something similar to this:

      {
        ref: Ref(Ref("tokens"), "292001047221633538"),
        ts: 1614732749110000,
        instance: Ref(Collection("Users"), "291901454585692675"),
        secret: "fnEEDWVnxbACAgQNBIxMIAIIKq1E5xvPPdGwQ_zUFH4F5Dl0neg"
      }
      

      Depending on the security requirements of the project, you have to decide how to handle the token’s secret. If this API was meant to be consumed by browsers, you might return the secret inside a secure cookie or an encrypted JSON Web Token (JWT). Or you might store it as session data somewhere else, like a Redis instance. For the purpose of this demo, you return it in the body of the HTTP response:

      Finally, add this bit of code to main.py, which will start the Flask application:

      main.py

      app.run(host=os.getenv('IP', '0.0.0.0'), port=int(os.getenv('PORT', 8080)))
      

      It’s important to specify the 0.0.0.0 address. Once deployed to the cloud, this application will run in a Docker container. It won’t be able to receive requests from remote clients if it is running on 127.0.0.1, which is the default address for Flask applications.

      This is the complete main.py file so far:

      main.py

      import os
      FAUNA_SECRET = os.environ.get('FAUNA_SECRET')
      
      import flask
      from flask import request
      
      import faunadb
      from faunadb import query as q
      from faunadb.client import FaunaClient
      
      app = flask.Flask(__name__)
      app.config["DEBUG"] = True
      
      @app.route('/signup', methods=['POST'])
      def signup():
      
          body = request.json
          client = FaunaClient(secret=FAUNA_SECRET)
      
          try:
              result = client.query(
                  q.create(
                      q.collection("Users"),
                      {
                          "data": {
                              "username": body["username"]
                          },
                          "credentials": {
                              "password": body["password"]
                          }
                      }
                  )
              )
      
              return {
                  "userId": result['ref'].id()
              }
      
          except faunadb.errors.BadRequest as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 409
      
      @app.route('/login', methods=['POST'])
      def login():
      
          body = request.json
          client = FaunaClient(secret=FAUNA_SECRET)
      
          try:
              result = client.query(
                  q.login(
                      q.match(
                          q.index("Users_by_username"),
                          body["username"]
                      ),
                      {"password": body["password"]}
                  )
              )
      
              return {
                  "secret": result['secret']
              }
      
          except faunadb.errors.BadRequest as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 401
      
      app.run(host=os.getenv('IP', '0.0.0.0'), port=int(os.getenv('PORT', 8080)))
      

      Save the file.

      To launch this server locally from your terminal, use the following command with the FAUNA_SECRET environment variable with the secret you obtained when creating the server key:

      • FAUNA_SECRET=your_fauna_server_secret python main.py

      After triggering that command, Flask will show a warning informing you it is running with a development WSGI server. This is fine for the purpose of this demo so you can safely ignore this warning.

      Test your API by making HTTP requests using the curl command. Open a new terminal window and run the following command:

      Create a user with the following command:

      • curl -i -d '{"user":"sammy", "password": "secretpassword"}' -H 'Content-Type: application/json' -X POST http://0.0.0.0:8080/signup

      You’ll see the following response, indicating a successful user creation:

      HTTP/1.0 200 OK
      Content-Type: application/json
      Content-Length: 37
      Server: Werkzeug/1.0.1 Python/3.9.2
      Date: Thu, 04 Mar 2021 01:00:47 GMT
      
      {
        "userId": "292092166117786112"
      }
      

      Now authenticate that user with this command:

      • curl -i -d '{"user":"sammy", "password": "secretpassword"}' -H 'Content-Type: application/json' -X POST http://0.0.0.0:8080/login

      You’ll get this successful response:

      HTTP/1.0 200 OK
      Content-Type: application/json
      Content-Length: 70
      Server: Werkzeug/1.0.1 Python/3.9.2
      Date: Thu, 04 Mar 2021 01:01:19 GMT
      
      {
        "secret": "fnEEDbhO3jACAAQNBIxMIAIIOlDxujk-VJShnnhkZkCUPKIHxbc"
      }
      

      Close the terminal window where you ran your curl commands and switch back to the terminal where your Python server is running. Stop your server by pressing CTRL+C.

      Now that the application is working, we’re going to add a private endpoint that requires users to be authenticated.

      Step 4 — Adding a Private Endpoint

      In this step, you’ll add a private endpoint to the API, which will require the user to be authenticated first.

      First, create a new route in the main.py file. This route will respond to the /things endpoint. Place it above the line that starts the server with the app.run() method:

      main.py

      @app.route('/things', methods=['GET'])
      def things():
      

      Next, in the /things route, instantiate the Fauna client:

      main.py

          userSecret = request.headers.get('fauna-user-secret')
          client = FaunaClient(secret=userSecret)
      

      Instead of using the server secret, this route is using the user’s secret from the fauna-user-secret HTTP header which is used to instantiate the Fauna client. By using the users’ secrets instead of the server secret, FQL queries will now be subject to the authorization rules we’ve configured previously in the dashboard.

      Then add this try block to the route to execute the query:

      main.py

          try:
              result = client.query(
                  q.map_(
                      q.lambda_("ref", q.get(q.var("ref"))),
                      q.paginate(q.documents(q.collection("Things")))
                  )
              )
      
              things = map(
                  lambda doc: {
                      "id": doc["ref"].id(),
                      "name": doc["data"]["name"],
                      "color": doc["data"]["color"]
                  },
                  result["data"]
              )
      
              return {
                  "things": list(things)
              }
      

      This executes an FQL query and parses the Fauna response into a serializable type that is then returned as a JSON string in the body of the HTTP response.

      Finally, add this except block to the route:

      main.py

          except faunadb.errors.Unauthorized as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 401
      

      If the request doesn’t contain a valid secret, a faunadb.errors.Unauthorized exception will be raised and a 401 response with the error information will be returned.

      This is the full code for the /things route:

      main.py

      @app.route('/things', methods=['GET'])
      def things():
      
          userSecret = request.headers.get('fauna-user-secret')
          client = FaunaClient(secret=userSecret)
      
          try:
              result = client.query(
                  q.map_(
                      q.lambda_("ref", q.get(q.var("ref"))),
                      q.paginate(q.documents(q.collection("Things")))
                  )
              )
      
              things = map(
                  lambda doc: {
                      "id": doc["ref"].id(),
                      "name": doc["data"]["name"],
                      "color": doc["data"]["color"]
                  },
                  result["data"]
              )
      
              return {
                  "things": list(things)
              }
      
          except faunadb.errors.Unauthorized as exception:
              error = exception.errors[0]
              return {
                  "code": error.code,
                  "description": error.description
              }, 401
      

      Save the file and run your server again:

      • FAUNA_SECRET=your_fauna_server_secret python main.py

      To test this endpoint, first obtain a secret by authenticating with valid credentials. Open a new terminal window and execute this curl command:

      • curl -i -d '{"username":"sammy", "password": "secretpassword"}' -H 'Content-Type: application/json' -X POST http://0.0.0.0:8080/login

      This command returns a succesful response, although the value for secret will be different:

      HTTP/1.0 200 OK
      Content-Type: application/json
      Content-Length: 70
      Server: Werkzeug/1.0.1 Python/3.9.2
      Date: Thu, 04 Mar 2021 01:01:19 GMT
      
      {
        "secret": "fnEEDb...."
      }
      

      Now hen do a GET request to /things using the secret:

      curl -i -H 'fauna-user-secret: fnEEDb...' -X GET http://0.0.0.0:8080/things
      

      You’ll get another successful response:

      HTTP/1.0 200 OK
      Content-Type: application/json
      Content-Length: 118
      Server: Werkzeug/1.0.1 Python/3.9.2
      Date: Thu, 04 Mar 2021 01:14:49 GMT
      
      {
        "things": [
          {
            "color": "Yellow",
            "id": "292079274901373446",
            "name": "Banana"
          }
        ]
      }
      

      Close your terminal window where you ran the curl commands. Return to your window where your server is running and stop the server with CTRL+C.

      Now that you have a working app, you’re ready to deploy it.

      Step 4 — Deploying to DigitalOcean

      In the final step of this tutorial, you will create an app on App Platform and deploy it from a GitHub repository.

      Before pushing the project to a Git repository, be sure to run the following command in the project’s folder:

      • pip freeze > requirements.txt

      This will create a requirements.txt file with the list of dependencies that need to be installed once the application is deployed.

      Now initialize your project directory as a Git repository:

      Now execute the following command to add files to your repository:

      This adds all the files in the current directory.

      With the files added, make your initial commit:

      • git commit -m "Initial version of the site"

      Your files will commit.

      Open your browser and navigate to GitHub, log in with your profile, and create a new repository called sharkopedia. Create an empty repository without a README or license file.

      Once you’ve created the repository, return to the command line to push your local files to GitHub.

      First, add GitHub as a remote repository:

      • git remote add origin https://github.com/your_username/sharkopedia

      Next, rename the default branch main, to match what GitHub expects:

      Finally, push your main branch to GitHub’s main branch:

      Your files will transfer. You’re now ready to deploy your app.

      Note: To be able to create an app on App Platform, you’ll first need to add a payment method to your DigitalOcean account.

      The application will run on a container which costs $5 per month, although only a few cents will be needed to test it out. Don’t forget to delete the application once you’re done or you’ll continue to be charged.

      Go to the Apps section of the DigitalOcean dashboard, and click on Launch Your App:

      Select the source for deployment. You will need to authorize DigitalOcean to read your Github repositories. Once you’ve authorized access, select the repository with your Python project and the branch that contains the version of the app you want to deploy:

      Selecting a repository and branch

      At this point, App Platform will determine that your project uses Python and will let you configure some application options:

      Configuring the app options

      Set the following options

      • Ensure the Type is Web Service.
      • Create aFAUNA_SECRET environment variable with your server secret.
      • Set the Run Command to python main.py.
      • Set the HTTP Port to 8080.

      Next, enter a name for your app and select a deploy region:

      Configuring the name of the app and deploy region

      Next, choose the Basic plan and Basic Size that costs $5 per month:

      Selecting the app plan

      After that, scroll down and click on Launch Your App.

      Once you’ve finished configuring the app, a container will be created and deployed with your application. This first-time initialization will take a couple of minutes, but subsequent deploys will be much faster.

      In the app’s dashboard you’ll see a green check mark to indicate the deploy process has finished successfully:

      App is running

      You will now be able to execute HTTP requests to the provided app domain. Execute the following command in your terminal, substituting your_app_name with your actual app name, to return a new secret for the sammy user:

      • curl -i -d '{"user":"sammy", "password": "secretpassword"}' -H 'Content-Type: application/json' -X POST https://your_app_name.ondigitalocean.app/login

      You’ll receive a response similar to the following:

      HTTP/1.0 200 OK
      Content-Type: application/json
      Content-Length: 70
      Server: Werkzeug/1.0.1 Python/3.9.2
      Date: Thu, 04 Mar 2021 01:01:19 GMT
      
      {
        "secret": "fnAADbhO3jACEEQNBIxMIAOOIlDxujk-VJShnnhkZkCUPKIskdjfh"
      }
      

      Your application is now up and running on Digital Ocean.

      Conclusion

      In this tutorial you created a Python REST API using Fauna as the data layer, and you deployed it to DigitalOcean App Platform.

      To keep learning about Fauna and dive deeper into FQL, check out the Fauna Documentation.



      Source link