Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for How to build an API with Ruby and Sinatra
Zuplo profile imageAdrian Machado
Adrian Machado forZuplo

Posted on • Originally published atzuplo.com

How to build an API with Ruby and Sinatra

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.

Dune quotes

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?

  1. Creating the project
  2. Adding the required gems
  3. Creating a MongoDB Atlas Account
  4. Installing MongoDB Shell and creating our quotes database
  5. Populating our quotes collection
  6. Testing our API
  7. Hosting our API for the world to see
  8. Creating a project on Zuplo
  9. Adding a Rate Limit
  10. Setting API Key Authentication
  11. 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
Enter fullscreen modeExit fullscreen mode

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'
Enter fullscreen modeExit fullscreen mode

To install all the dependencies, run the following command:

bundleinstall
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

Installing MongoDB Shell and creating our quotes database

We’ll useHomebrew (Brew) for the installation process.:

$brewinstallmongosh
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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)
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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"
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

inserting quotes

We can verify it by loading our collection in the MongoDB shell:

$mongosh"mongodb+srv://blagcluster.<URL>.mongodb.net/"--apiVersion 1--username <USER>--quiet
Enter fullscreen modeExit fullscreen mode

verifying quote insertion

Once we’re logged in, we can check our collection by running::

$usequotes$db.quotes.find()
Enter fullscreen modeExit fullscreen mode

quote collection

Testing our API

Now that we have data to work with, let’s start our server and test the API.

bundleexecruby server.rb
Enter fullscreen modeExit fullscreen mode

Open your favorite web browser and navigate tohttp://localhost:4567/api/v1/quotes:

Ruby API response

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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:

Get character response

Get quote response

Limiting API results

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.

Render web service creation

Connecting to a git repo

Don’t forget to update the start command to correctly call ourserver.rb file:

Setting the state command

It’s crucial to click onConnect and copy the three static IP addresses used by Render:

Copying IP addresses

We’ll use those three IPs in MongoDB Atlas to allow access underNetwork Access; otherwise, the connection will be denied:

Network access to MongoDB Atlas

Once Render finishes deploying our API, we’ll be able to access the main entry point using:

https://dunequotes.onrender.com
Enter fullscreen modeExit fullscreen mode

We can then call an endpoint using:

https://dunequotes.onrender.com/api/v1/quotes?character=Pau&limit=2
Enter fullscreen modeExit fullscreen mode

API response

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.

Zuplo logo

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.

Creating a Zuplo project

Once the project is created, we’ll be greeted with this screen. From here, simply pressStart Building:

Start building a Zup

To start enhancing our API, click onroutes.oas.json:

Navigating to routes.oas.json

Next, we need to add a route, which Zuplo will manage:

Adding a route

The most important fields here arePath andForward to:

Configuring the path

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.

Saving your changes

The build process is blazing fast. Clicking theTest button allows us to, no pun intended, test our API integration.

Testing the API

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.

Adding a policy

We need to add a new policy on the request side. Since we’ll be dealing with many policies, simply typerate and selectRate Limiting:

Selecting rate limiting

All we need to do here is pressOk. Easy, right?

Adding rate limiting

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.

Saving rate limiting

Exceeding the request limit will temporarily block further data requests. We’ll need to wait a minute before trying again.

Testing rate limiting

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:

Selecting API key authentication

There’s not much to do here—just pressOK.

Configuring API key authentication

It’s important to move theapi-key-inbound policy to the top:

Rearranging policies

If we save it and try testing the API, access will be denied:

Testing API key authentication rejection

At the top of the screen, clickServices, then selectConfigure underAPI Key Service:

Navigating the services tab

We need to create a consumer, which will function as a token:

Creating a consumer

We need to set a name for the subject and specify the email or emails associated with the key:

Adding a consumer

Once we clickSave consumer, an API key will be generated. We can either view it or copy it:

Saving the API consumer

Now, we can go back toCoderoutes.oas.json to test our API. Here, we need to add the authorization header and pass theBearer token along with the API key:

Testing the API key

Success once again! It’s working as expected! 🥳🥳🥳

If you think that doing this manually is not for you, read this:

API key service API

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:

API developer portal link

Here, we can see that every route we create will be added to the Developer Portal:

API developer portal

If we log in, we can see our API keys:

Developer portal post-login

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

More fromZuplo

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp