
This article is written by Alvaro, a member of the Zuplo community and > longtime API builder. You can check out more of his work >here. All opinions expressed are his own.
Have you ever heard of Frank Herbert’sDune saga? I’m not talking about the movie—though it was pretty awesome. I’m talking about the books 🤓, which are on a whole other level of amazing.
If you’ve read the books (and if you haven’t, you absolutely should), you know they’re packed with incredible quotes. Of course, it’s tough to remember them all, so we’re going to create a small API project using Ruby and Sinatra to make it easier. Later, we’ll use Zuplo to take our API to the next level of awesomeness 😎.
What are we going to do today?
- Creating the project
- Adding the required gems
- Creating a MongoDB Atlas Account
- Installing MongoDB Shell and creating our quotes database
- Populating our quotes collection
- Testing our API
- Hosting our API for the world to see
- Creating a project on Zuplo
- Adding a Rate Limit
- Setting API Key Authentication
- Developer Documentation Portal
Creating the project
First, create a folder namedduneQuotes and add a file calledserver.rb inside it. Since this is a straightforward project, we’ll keep all our source code in a single file for simplicity.
$mkdirduneQuotes&&cdduneQuotes$nano server.rb
Adding the required gems
For this project, we’ll use Sinatra, as mentioned earlier, along withMongoDB Atlas and a few additional gems. To get started, create a file namedGemfile and add the following:
# Gemfile source 'https://rubygems.org'gem'sinatra'gem'mongoid'gem'sinatra-contrib'gem'rackup'gem'puma'gem'ostruct'gem'json'
To install all the dependencies, run the following command:
bundleinstall
Creating a MongoDB Atlas account
Go here and create yourfree account. It’s straightforward but here’s a littleguide just in case.
In the end, you’re going to receive a string like this one:
mongodb+srv://<USER>:<PASSWORD>@blagcluster.<URL>.mongodb.net/quotes?retryWrites=true&w=majority&appName=BlagCluster
Installing MongoDB Shell and creating our quotes database
We’ll useHomebrew (Brew) for the installation process.:
$brewinstallmongosh
After installation, create a file namedmongoid.yml and add the following content::
development:clients:default:uri:"mongodb+srv://<USER>:<PASSWORD>@blagcluster.<URL>.mongodb.net/quotes?retryWrites=true&w=majority&appName=BlagCluster"options:auth_mech::scramserver_selection_timeout:5options: log_level::warn
This will help us establish the connection with MongoDB Atlas.
This will create the database, and in theserver.rb file, we’ll define the collection (or document):
# server.rbrequire'sinatra'require"sinatra/namespace"require'mongoid'require'json'Mongoid.load!"mongoid.config"classQuoteincludeMongoid::Documentfield:quote,type:Stringfield:character,type:Stringvalidates:quote,presence:truevalidates:character,presence:trueindex({character:'text'})scope:character,->(character,limit=nil){query=where(character:/^#{character}/)limit?query.limit(limit):query}endget'/'do'🐭🌖 Dune Quotes 🐭🌖'endnamespace'/api/v1'dobeforedocontent_type'application/json'endget'/quotes'doquotes=Quote.allquotes.to_jsonendend
Before running this (though it will be empty since we haven’t populated our collection yet), let’s take a moment to analyze the code:
Mongoid.load!("mongoid.yml",:development)
We’re loading our MongoDB configuration file:
classQuoteincludeMongoid::Documentfield:quote,type:Stringfield:character,type:Stringvalidates:quote,presence:truevalidates:character,presence:trueindex({character:'text'})scope:character,->(character,limit=nil){query=where(character:/^#{character}/)limit?query.limit(limit):query}end
We’re defining our collection. We’re going to have two fields, quote and character. Both need to be present at the time of data insertion. We’re going to index our collection by character. The last part means that we want to use Regular Expressions to find a character’s quote without needing to specify its full name, also, it means that we can specify how many records we want to get back.
get'/'do'🐭🌖 Dune Quotes 🐭🌖'end
This is what we’ll see when we call the main API.
namespace'/api/v1'dobeforedocontent_type'application/json'endget'/quotes'doquotes=Quote.allquotes.to_jsonend
We’ll create a namespace to enable versioning for our API, and ensure that the responses are formatted as JSON.
Our first endpoint will fetch and return all quotes.
Populating our quotes collection
Let’s create a YAML file namedQuotes.yaml with the following content::
-quote:"Hewhocandestroyathing,controlsathing."character:"PaulAtreides"-quote:"Allpathsleadintodarkness."character:"PaulAtreides"-quote:"Theeyethatlooksaheadtothesafecourseisclosedforever."character:"PaulAtreides"-quote:"Motivatingpeople,forcingthemtoyourwill,givesyouacynicalattitudetowardshumanity.Itdegradeseverythingittouches."character:"LadyJessica"-quote:"Whenwetrytoconcealourinnermostdrives,theentirebeingscreamsbetrayal."character:"LadyJessica"-quote:"Onceyouhaveexploredafear,itbecomeslessterrifying.Partofcouragecomesfromextendingourknowledge."character:"DukeLetoAtreides"-quote:"Respectforthetruthisthebasisforallmorality.Somethingcannotemergefromnothing."character:"DukeLetoAtreides"-quote:"Theslowbladepenetratestheshield."character:"GurneyHalleck"-quote:"Imustnotletmypassioninterferewithmyreason.Thatisnotgood.Thatisbad."character:"PiterDeVries"-quote:"Imustnotfear.Fearisthemind-killer."character:"BeneGesserit"-quote:"Thereisnoescape—wepayfortheviolenceofourancestors."character:"PaulMuad’Dib"
Next, we’ll create a script to load our quotes:
require'./server.rb'require'yaml'quotes=YAML.load_file("Quotes.yaml")quotes.eachdo|quote_data|beginquote=Quote.new(quote:quote_data["quote"],character:quote_data["character"])quote.saveputs"Document inserted successfully."rescueMongo::Error::OperationFailure=>eputs"Insertion failed:#{e.message}"endend
This will load ourserver.rb file, load the YAML file, and then iterate through each entry to perform an insert using thequote andcharacter.
You can run it by typing::
$ruby Load_Quotes.rb
We can verify it by loading our collection in the MongoDB shell:
$mongosh"mongodb+srv://blagcluster.<URL>.mongodb.net/"--apiVersion 1--username <USER>--quiet
Once we’re logged in, we can check our collection by running::
$usequotes$db.quotes.find()
Testing our API
Now that we have data to work with, let’s start our server and test the API.
bundleexecruby server.rb
Open your favorite web browser and navigate tohttp://localhost:4567/api/v1/quotes:
Success! It’s working as expected, but it’s a bit too simple. Let’s add a few more endpoints to make it more useful and feature-rich:
namespace'/api/v1'dobeforedocontent_type'application/json'endget'/quotes'doquotes=Quote.all[:quote,:character].eachdo|filter|quotes=quotes.send(filter,params[filter])ifparams[filter]endifparams[:limit]limit=params[:limit].to_iquotes=quotes.limit(limit)endquotes.to_jsonendget'/quotes/id/:id'do|id|quote=Quote.where(id:id).firsthalt(404,{message:'Quote Not Found on Arrakis'}.to_json)unlessquotequote.to_jsonendget'/quotes/random'doquote=Quote.collection().aggregate([{'$sample'=>{'size'=>1}}])quote.to_jsonendend
Let’s review the code before running it:
get'/quotes'doquotes=Quote.all[:quote,:character].eachdo|filter|quotes=quotes.send(filter,params[filter])ifparams[filter]endifparams[:limit]limit=params[:limit].to_iquotes=quotes.limit(limit)endquotes.to_jsonend
We’re adding the ability to filter quotes by character. For example, to retrieve all quotes by Paul Atreides, you can enterPau,Paul, orPaul A. Additionally, we’re introducing a limit option, allowing you to specify how many records to retrieve, such as1,2, or more.
get'/quotes/id/:id'do|id|quote=Quote.where(id:id).firsthalt(404,{message:'Quote Not Found on Arrakis'}.to_json)unlessquotequote.to_jsonend
If we know the ID, we can use it to select a specific quote. If an incorrect ID is provided, the quote won’t be found—just like water on Arrakis 🤓:
get'/quotes/random'doquote=Quote.collection().aggregate([{'$sample'=>{'size'=>1}}])quote.to_jsonend
Sometimes, we might just want to fetch a random quote—which makes perfect sense, as always selecting the same one would be quite dull.
Alright, let’s test the new functionalities:
Excellent! It works as expected, but only on our local machine. Wouldn’t it be amazing to share it with the world?
First, we need to upload our project to GitHub. Keeping it private won’t cause any issues.
Hosting our API for the world to see
When it comes to hosting an API, there are plenty of options available. For this particular API, we’ll useRender.
We need to create a new web service and select the repository we want to use—in this case,duneQuotes.
Don’t forget to update the start command to correctly call ourserver.rb file:
It’s crucial to click onConnect and copy the three static IP addresses used by Render:
We’ll use those three IPs in MongoDB Atlas to allow access underNetwork Access; otherwise, the connection will be denied:
Once Render finishes deploying our API, we’ll be able to access the main entry point using:
https://dunequotes.onrender.com
We can then call an endpoint using:
https://dunequotes.onrender.com/api/v1/quotes?character=Pau&limit=2
Are we done yet? Yes and no. If we’re happy with it as is—simple, unsecured, and, well, a bit amateurish—then we’re good. But if we want to make it cooler without too much effort, we can start using Zuplo and level up the experience.
Creating a project on Zuplo
After creating aZuplo account, we’ll need to set up a new project. We’ll name itdune-quotes, select an empty project, and then create it. Wondering why Zuplo? Imagine having to build rate limits, authentication, monetization, and other features entirely on your own. That’s a lot of work and hassle. Zuplo simplifies all of that, making it a breeze.
Once the project is created, we’ll be greeted with this screen. From here, simply pressStart Building:
To start enhancing our API, click onroutes.oas.json:
Next, we need to add a route, which Zuplo will manage:
The most important fields here arePath andForward to:
After configuring the route, we need to save it. The save button is located at the bottom, or you can press[Command + S] on a Mac.
The build process is blazing fast. Clicking theTest button allows us to, no pun intended, test our API integration.
Success! 🥳🥳🥳 Our Zuplo integration is working perfectly!
Adding a Rate Limit
Most likely, we don’t want people abusing our API or attempting to take down our server with DDoS attacks. While coding a rate limit policy isn’t difficult, there are many factors to consider—so why bother? Let’s have Zuplo manage that for us.
We need to add a new policy on the request side. Since we’ll be dealing with many policies, simply typerate and selectRate Limiting:
All we need to do here is pressOk. Easy, right?
Our rate limit has been added. Now, we just need to save and run the test three times. On the third attempt, we’ll hit the rate limit. Of course, we can adjust this in the policy, as shown in the image above, whererequestsAllowed is set to 2.
Exceeding the request limit will temporarily block further data requests. We’ll need to wait a minute before trying again.
So far, so good—but what if we want to prevent unauthorized access to our API? We’ll need to implement some sort of authentication. While it’s not overly difficult to build, it involves multiple steps, configurations, and testing. Wouldn’t it be great to let Zuplo handle those details for us? That way, we can focus solely on our API.
Setting API Key Authentication
We need to navigate back to thePolicies section and add a new policy—this time,API Key Authentication:
There’s not much to do here—just pressOK.
It’s important to move theapi-key-inbound policy to the top:
If we save it and try testing the API, access will be denied:
At the top of the screen, clickServices, then selectConfigure underAPI Key Service:
We need to create a consumer, which will function as a token:
We need to set a name for the subject and specify the email or emails associated with the key:
Once we clickSave consumer, an API key will be generated. We can either view it or copy it:
Now, we can go back toCode →routes.oas.json to test our API. Here, we need to add the authorization header and pass theBearer token along with the API key:
Success once again! It’s working as expected! 🥳🥳🥳
If you think that doing this manually is not for you, read this:
And that’s how you enhance your API with Zuplo 😎 But wait, there’s more! As always, we have a cherry on top.
Developer Documentation Portal
If we click onGateway at the bottom of the screen, we’ll see a link to the Developer Portal:
Here, we can see that every route we create will be added to the Developer Portal:
If we log in, we can see our API keys:
So, what are you waiting for? Give Zuplo a try! There are tons of features that will make your API stand out, and your development team will be happy they won’t have to maintain everything on their own.
If you’d like to check out the source code for the API, here’s a clean version without the MongoDB Atlas keys,duneQuotesPublic.
If you want to learn more about Zuplo, check out theirdocumentation—it’s a fantastic resource.
Now, what can you build with Zuplo? 😎
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse