Simplifying Backend APIs with Macaw Framework: A CRUD Tutorial
Aria Diniz
Posted on May 5, 2023
If you're tired of configuring a lot of parameters and things you wouldn't even use when creating simple backend APIs, you might want to check out Macaw Framework. In my previous post, I introduced Macaw Framework, a lightweight and flexible web framework. Now, I'd like to show you how to use Macaw Framework to build a simple CRUD application.
It's worth noting that, despite being production ready, Macaw Framework is still in its first beta version. I encourage you to try it out, give feedback, and report any issues you encounter. Your feedback will be invaluable in helping me to improve the framework and make it even better for the community.
In this article, I'll guide you through the steps to build a simple CRUD application using Macaw Framework. You can find the code for this application on GitHub here. If you're not familiar with Macaw Framework, I encourage you to read my previous post on the topic to get a better understanding of how the framework works.
Macaw Framework is designed to be simple and easy to use, with sensible defaults that make it easy to get started. It's also highly customizable, with a wide range of options for configuring your application to meet your needs. Whether you're building a simple API or a complex web application, Macaw Framework can help you get the job done quickly and efficiently.
Step 0: Create and Configure Your application.json
File
Before we dive into building our CRUD application, we need to create and configure our application.json
file. This file will contain configuration options for our Macaw Framework application.
Create a new file called application.json
in the root directory of your project and add the following contents:
{
"macaw": {
"threads": 10,
"bind": "0.0.0.0",
"port": 8080,
"cache": {
"cache_invalidation": 10,
"ignore_headers": [
"Postman-Token"
]
},
"prometheus": {
"endpoint": "/metrics"
},
"rate_limiting": {
"window": 10,
"max_requests": 10
}
}
}
This file contains various options that will be used by Macaw Framework to configure your application. For example, the threads
option specifies the number of threads that should be used by the application, while the cache
option configures caching behavior. You can adjust these options to suit the needs of your application.
Once you've created your application.json
file, you're ready to start building your CRUD application using Macaw Framework!
Step 1: Set Up Your Environment
To get started, create a new directory for your project and navigate into it. Then, add a Gemfile with the following content:
# frozen_string_literal: true
source "https://rubygems.org"
gem "sqlite3"
gem "yaml"
gem "json"
gem "macaw_framework"
gem "activerecord"
For this demo project I'm using a SQLite3 database. You can find the database and everything else in the project's repository.
Next, run bundler in order to install the dependencies:
bundle install
Using Bundler makes it easier to manage dependencies for your project, and ensures that all team members are using the same versions of the required gems. Once you have your dependencies installed, you're ready to start building your application with Macaw Framework!
Step 2: Define Your Person Model
Create a new file called person.rb
in a subdirectory called lib/entity. This file will define your Person model, which represents a person in your database.
class Person < ActiveRecord::Base
table_name = 'people'
end
Step 3: Create Your App File
Create a new file called app.rb
in the root directory of your project. This file will be the entry point for your application, and it will define your Macaw Framework endpoints.
# frozen_string_literal: true
require 'macaw_framework'
require 'active_record'
require 'yaml'
require_relative './lib/entity/person'
require_relative './lib/errors/unprocessable_body_error'
# Configuring ActiveRecord
db_config = File.open('./db/database.yaml')
ActiveRecord::Base.establish_connection(YAML.safe_load(db_config, aliases: true))
# Instantiating MacawFramework Class
server = MacawFramework::Macaw.new
Step 4: Define Your GET Endpoint
The first endpoint we'll define will allow us to retrieve a list of all people in the database. Add the following code to your app.rb
file:
# Defining a GET endpoint to list all persons in the database
server.get('/people', cache: true) do |_context|
return JSON.pretty_generate(Person.all.as_json), 200, {"Content-Type" => "application/json", "random-header" => "random value"}
end
Step 5: Define Your GET by Id Endpoint
The second endpoint we'll define will allow us to retrieve a specific person from the database by their ID. Add the following code to your app.rb
file:
# Defining a GET endpoint to recover person with provided id
server.get('/people/:person_id') do |context|
return JSON.pretty_generate(Person.where(id: context[:params][:person_id]).first.as_json), 200
end
Step 6: Define Your POST Endpoint
The third endpoint we'll define will allow us to create a new person in the database. Add the following code to your app.rb
file:
server.post('/add_new_person') do |context|
begin
parsed_body = JSON.parse(context[:body])
name = parsed_body['name']
age = parsed_body['age']
raise UnprocessableBodyError if name.nil? || age.nil?
Person.create!(name: name, age: age)
return JSON.pretty_generate({ message: "Person created with success!!!" }), 201
rescue UnprocessableBodyError => e
return JSON.pretty_generate({ error: e }), 422
rescue StandardError => e
return JSON.pretty_generate({ error: e }), 500
end
end
This endpoint will create a new Person
record in the database based on the provided request body, which should be a JSON object with name
and age
properties. If the request body is missing either property, we'll raise our custom UnprocessableBodyError
and return a 422 Unprocessable Entity
status code with an error message.
Note that we're also raising a new custom error class, UnprocessableBodyError
, if the request body is missing the required properties. To define this error class, create a new file called unprocessable_body_error.rb
in your lib/errors
directory with the following contents:
class UnprocessableBodyError < StandardError
def initialize(
msg = 'Please inform a body on your request in JSON format with a "name" and "age" properties'
)
super
end
end
This error will be rescued in our PATCH
and POST
endpoint and return a 400 Bad Request
status code with an error message if the request body is missing the required properties.
Step 7: Define Your PATCH Endpoint
The fourth endpoint we'll define will allow us to update an existing person in the database. Add the following code to your app.rb
file:
server.patch('/people/:person_id') do |context|
raise UnprocessableBodyError.new unless context[:params][:person_id]
body = JSON.parse(context[:body])
name = ActiveRecord::Base.connection.quote(body["name"])
age = ActiveRecord::Base.connection.quote(body["age"])
Person.update(
context[:params][:person_id].to_i,
name: name,
age: age
)
[JSON.pretty_generate({ message: "Updated person #{context[:params][:person_id]}" }), 200]
rescue UnprocessableBodyError
["Person with id #{context[:params][:person_id]} does not exist", 400]
rescue StandardError => e
[JSON.pretty_generate(e.message), 500]
end
Step 8: Define Your DELETE Endpoint
The fifth and final endpoint we'll define will allow us to delete an existing person from the database. Add the following code to your app.rb
file:
# Defining a DELETE endpoint to delete an existing person in the database
server.delete('/delete_person') do |context|
begin
parsed_body = JSON.parse(context[:body])
id = parsed_body['id']
raise UnprocessableBodyError.new('Please inform a JSON body with an "id" parameter') if id.nil?
person = Person.find_by(id: id.to_i)
if person.nil?
return JSON.pretty_generate({ error: "Person with ID #{id} not found." }), 404
else
person.destroy
return JSON.pretty_generate("Person with ID #{id} deleted."), 200
end
rescue UnprocessableBodyError => e
return JSON.pretty_generate({ error: e.message }), 422
rescue StandardError => e
return JSON.pretty_generate({ error: e }), 500
end
end
Step 9: Start Your Server
Finally, add the following code to your app.rb
file to start the server:
# Start Server
server.start!
Now that you've defined all of your endpoints, you're ready to start your server and test your application! You can do this by running the following command:
ruby app.rb
Your server will start running on http://localhost:8080/. You can use tools like Postman or cURL to send requests to your endpoints and test your application.
Now that we've defined all of our endpoints, we're ready to start our server and test our application!
Posted on May 5, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.