Backend-as-a-service (BaaS) is revolutionizing the way we think about backend development, offering a cost-effective, scalable, and secure solution for developers. BaaS providers like Altogic offer a wide range of services, including database management, user authentication, and file storage. These services are available through a simple API, which allows developers to focus on the frontend of their application. On the other hand, developers who choose to build their backend from scratch have to deal with the complexity of managing their own servers, databases, and other infrastructure.
In this article, we will compare Altogic to DIY backend development by looking at simple todo apps. We will build a todo app using Express.js and MongoDB, and then we will build the same app using Altogic. We will compare the two approaches in terms of cost, time, and complexity.
The DIY Approach
Let’s start by building a simple todo app using Express.js and MongoDB. We will use the Express.js framework to build the backend, and we will use MongoDB as our database. We will also use Mongoose to model our data.
Setting up the Backend
First, we need to create a new Node.js project. We will use the Express.js framework to build our backend. We will also use Mongoose to model our data.
mkdir todo-app
cd todo-app
npm init -y
npm install express
This will install Express and its dependencies in your project's node_modules
folder. Once the installation is complete, you can create a new file called app.js
and start writing your Express code.
The first thing we need to do is require the Express module and create an instance of the app:
const express = require("express");const app = express();app.use(express.json());app.use(express.urlencoded({ extended: true }));
We can then start our app by listening on a specific port:
app.listen(3000, () => { console.log("Server listening on port 3000");});
This will start our app and print a message to the console indicating that the server is listening on port 3000.
Adding routes to our app
Routes are a fundamental part of any Express app. They are used to define the endpoints that our app will respond to. Let's create a basic route for our app that will respond with a simple message:
app.get("/", (req, res) => { res.send("Hello World!");});
In this code, we're defining a route for the root of our app (/)
. When a GET request is made to this endpoint, we're responding with the message "Hello World!".
To test our app, we can use the node app.js
command. This will start our app and print a message to the console indicating that the server is listening on port 3000.
Creating a database
Now that we have our basic Express app up and running, we can start building the CRUD functionality. The first step is to create a database. For this tutorial, we'll be using MongoDB, a popular NoSQL database.
To get started, you'll need to install the mongoose
module, which will allow us to interact with our MongoDB database:
npm install mongoose
Once the installation is complete, we can create a new file called todo.js
and start writing our database code:
const mongoose = require("mongoose");mongoose.connect( "mongodb+srv://<username>:<password>@cluster0.1pvj.mongodb.net/?retryWrites=true&w=majority", { useNewUrlParser: true, useUnifiedTopology: true, });const db = mongoose.connection;db.on("error", console.error.bind(console, "connection error:"));db.once("open", () => { console.log("Database connected");});
This code connects to a MongoDB database running locally on our machine and logs a message to the console when the connection is successful.
tip
For this tutorial we're using MongoDB Atlas, a cloud-hosted MongoDB database. You can use any MongoDB database you like. If you don't have a MongoDB database, you can create a free account on MongoDB Atlas.
Defining a schema
Next, we need to define a schema for our data. A schema is a blueprint that defines the structure of our data. In this case, we'll be creating a schema for a simple "todo" app.
Let's open the file called todo.js
and add the following code:
const todoSchema = new mongoose.Schema({ title: { type: String, required: true, }, completed: { type: Boolean, default: false, }, created_at: { type: Date, default: Date.now, },});const Todo = mongoose.model("Todo", todoSchema);module.exports = Todo;
This code defines a schema for our "todo" app that includes a title
, completed
flag, and created_at
timestamp. The title
field is required, and the completed
field has a default value of false
. The created_at
field has a default value of Date.now
, which will automatically set the timestamp when a new todo is created.
Adding CRUD functionality
Now that we have our database and schema set up, we can start building the CRUD functionality for our app. We'll need to define routes for each of the CRUD operations: create, read, update, and delete.
Create operation
Let's start by defining a route for creating new todos. We'll need to parse the JSON data that's sent in the request body, create a new Todo
document using the schema we defined earlier, and then save it to the database.
const Todo = require("./todo");app.post("/todos", (req, res) => { try { const { title } = req.body; if (!title) { return res.status(400).send("Title is required"); } const todo = new Todo({ title: req.body.title, completed: req.body.completed, }); todo.save(); res.send(todo); } catch (err) { return res.status(500).send(err.message); }});
In this code, we're defining a route for creating new todos (/todos)
. We're using app.post
to define a POST endpoint. We're parsing the title
field from the request body and checking that it exists. If it doesn't, we're sending a 400 error response.
If the title
field does exist, we're creating a new Todo
document using the schema we defined earlier, and then saving it to the database using the save
method. If there's an error, we're sending a 500 error response. If everything goes well, we're sending the newly created todo document in the response.
You can use below code to test the create operation:
curl -X \ POST -H \ "Content-Type: application/json" -d '{"title": "Buy milk"}' http://localhost:3000/todos
Read operation
Next, we need to define a route for reading todos. We'll need to fetch all the todos from the database and send them in the response.
app.get("/todos", async (req, res) => { try { const todos = await Todo.find(); res.send(todos); } catch (err) { return res.status(500).send(err.message); }});
In this code, we're defining a route for retrieving all todos (/todos)
. We're using app.get
to define a GET endpoint. We're using the find
method to retrieve all Todo
documents from the database. If there's an error, we're sending a 500 error response. If everything goes well, we're sending an array of todos
in the response.
Update operation
Now, let's define a route for updating existing todos. We'll need to parse the JSON data that's sent in the request body, find the existing Todo
document by ID, update it with the new data, and then save it to the database.
app.put("/todos/:id", async (req, res) => { try { const { id } = req.params; const { title, completed } = req.body; const todo = await Todo.findByIdAndUpdate(id, { title, completed, }); res.send(todo); } catch (err) { return res.status(500).send(err.message); }});
In this code, we're defining a route for updating existing todos (/todos/:id)
. We're using app.put
to define a PUT endpoint. We're parsing the id
, title
, and completed
fields from the request body. We're using the findByIdAndUpdate
method to find the existing Todo
document by ID, update it with the new title
and completed
fields, and return the updated document in the response. If there's an error, we're sending a 500 error response.
Delete operation
Finally, let's define a route for deleting existing todos. We'll need to find the existing Todo
document by ID and then delete it from the database.
app.delete("/todos/:id", async (req, res) => { try { const { id } = req.params; const todo = await Todo.findByIdAndDelete(id); res.send(todo); } catch (err) { return res.status(500).send(err.message); }});
In this code, we're defining a route for deleting existing todos (/todos/:id)
. We're using app.delete
to define a DELETE endpoint. We're parsing the id field from the request parameters. We're using the findByIdAndDelete
method to find the existing Todo
document by ID and then delete it from the database. If there's an error, we're sending a 500 error response. If everything goes well, we're sending a 204 status code to indicate success with no content in the response.
info
In this tutorial we will not be covering the testing of the API endpoints. However, you can use the Postman, Insomnia, or curL command-line tool to test the API endpoints. Both of these tools are available for Windows, Mac, and Linux
Here is example curl commands to test the API endpoints:
curl request to create a todo
curl -X \ POST -H \ "Content-Type: application/json" -d '{"title": "Buy milk"}' http://localhost:3000/todos
curl request to update a todo
curl -X \ PUT -H \ "Content-Type: application/json" -d '{"title": "Buy milk", "completed": true}' http://localhost:3000/todos/5f9f1b9b9c9c1c1c1c1c1c1c
curl request to delete a todo
curl -X \ DELETE http://localhost:3000/todos/5f9f1b9b9c9c1c1c1c1c1c1c
curL request to get all todos
curl http://localhost:3000/todos
Bonus: Implementing a Logging Middleware Function
Middleware functions in Express are functions that have access to the request and response objects and can modify them or execute any additional code as needed. Middleware functions can be used to perform tasks like logging, authentication, handling errors, parsing request bodies, and more.
In this section, we'll highlight a few commonly used middleware functions in Express and demonstrate how to implement one of them in our todo app.
Body Parser
The body-parser
middleware function parses the request body and makes it available in the req.body
property. This is useful for parsing JSON data that's sent in the request body.
To use the body-parser
middleware function, we need to install it using npm:
npm install body-parser
Once installed, we can import it in our app.js
file and use it as a middleware function:
const bodyParser = require("body-parser");app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: true }));
Morgan
Another commonly used middleware function in Express is the morgan
middleware. This middleware is used for logging HTTP requests and responses. The morgan
middleware supports different formats for logging, including the dev
format, which logs HTTP requests and responses in a developer-friendly format.
To use the morgan
middleware, we can install it using the npm install morgan
command and then add it to our app using the app.use()
method:
const express = require("express");const morgan = require("morgan");const bodyParser = require("body-parser");const Todo = require("./models/todo");const app = express();app.use(morgan("dev"));app.use(bodyParser.json());// Your app code goes hereapp.use((err, req, res, next) => { console.error(err.stack); res.status(500).send("Something broke!");});module.exports = app;
In this code, we've added the morgan middleware using app.use(morgan('dev')). The 'dev' argument tells morgan to log HTTP requests and responses to the console in the dev format.
We've also added the body-parser middleware using app.use(bodyParser.json()), and defined our CRUD routes as before.
Finally, we've added an error handling middleware function at the end of our app.js file, which logs errors to the console and sends a generic error response to the client.
Logging Middleware Function
In this section, we'll implement a custom logging middleware function that logs the HTTP request method, URL, and response status code to the console.
app.use((req, res, next) => { const start = Date.now(); const { method, url } = req; res.on("finish", () => { const duration = Date.now() - start; const { statusCode } = res; const log = `${method} ${url} ${statusCode} ${duration}ms`; console.log(log); }); next();});
In this code, we've implemented a custom middleware function that logs the HTTP request method, URL, response status, and response time (in milliseconds) to the console. The middleware function works by setting a timestamp at the beginning of the request (const start = Date.now()
), then logging the request details after the response has been sent (res.on('finish', () => {...}
)). We're using the res.statusCode
property to log the HTTP response status.
With this logging middleware function added, we can now see detailed logs of all HTTP requests and responses in our app's console.
Now that we've implemented a logging middleware function, we can use Railway, Heroku, or a cloud platform as a service, to deploy our todo app to the cloud. In this tutorial, we'll skip the deployment process. You can check out the Railway documentation for more information on how to deploy your app to Railway.
Let's now take a look at how we can build the same todo app using Altogic.
Altogic
Altogic is a backend-as-a-service platform that enables developers to build and deploy serverless applications. Altogic provides a serverless runtime environment, a serverless database, and a serverless API gateway. Altogic also provides a serverless function builder that enables developers to build serverless functions using a visual editor.
In this section, we'll use Altogic to build same todo app that we built in this tutorial. We'll only use the Altogic Designer to build the same API endpoints that we built in the previous sections.
Creating a New Altogic Project
After creating an account, you will see the workspace and repository page. You can create a new app by clicking the + New app
button.

Creating a Todo Model
In the Altogic Designer, let's visit the Models tab and click the + New
button.

In the Create Model dialog, we can enter a model name and enable timestamps. Then, we can click the Next button.

In the next dialog, we can enable CRUD operations for the model. Then, we can click the Create button.

Once the model is created, we can click the todo model to add fields to the model.

In the todo model, we can add the following fields:
- title: Text (required)
- completed: boolean (default: false)

With using the visual designer, we can set the field type, whether the field is required, and the default value for the field. We can also set the field to be unique, and we can set the field to be indexed. Altogic also provides numerous field types, including text, number, boolean, date, and more (see the Altogic documentation for a full list of field types and defining data models).
As we selected the creating default endpoints to make CRUD operation for the todo model, Altogic automatically created the following API endpoints for us:

Now we have the same todo functionality that we build in the express app. By using Altogic, we can build the same API endpoints in a fraction of the time that it would never requires us to deploy and manage a server or database. Even you can extend the API functionality by visiting the Services tab and clicking the Service Name or adding a new service.

When you click the Service Name, you will be able to see the service flow and you design the service flow by using core nodes and connectors. Also you can use marketplace nodes to integrate with other 3rd party services just simply by dragging and dropping the node to the service designer and connecting the node with connectors.
With using Altogic the logging middleware function that we built in the express app, we don't need to implement because Altogic provides a built-in logging middleware function that logs all HTTP requests and responses to the Altogic Watcher. The Altogic Watcher is a real-time log viewer that enables developers to monitor the logs of their Altogic apps.

In the previous todo app with express server, if we want to test the API endpoints, we have to use a tool like Postman. With Altogic, we can test the API endpoints using the Altogic Tester. And we can also display the service logs and display the duration of the each step in the service flow.

Summary
In summary, building the same todo app with Altogic is faster and easier than using Node.js and Express. With Altogic, we could create a fully-functional todo app without the need to set up a database or deploy to a cloud platform like Heroku or Railway. With Node.js and Express, we had to create and configure a MongoDB database, set up a server, handle routing and HTTP requests, and deploy the application to a cloud platform. This process can be time-consuming and complex, especially for developers who are new to app development.
On the other hand, Altogic provides a drag-and-drop interface that simplifies the app development process. By using pre-built nodes and integrations, developers can create a fully-functional application in a matter of hours or days, instead of weeks or months. Additionally, Altogic's built-in cloud infrastructure eliminates the need to set up and configure servers, databases, or other infrastructure components, which can save developers a significant amount of time and effort.
Furthermore, Altogic offers a range of customization options that allow developers to tailor their applications to their specific needs. With a variety of integrations, nodes and supporting custom javascript, developers can create an app that meets their unique functionality requirements without writing a single line of code. Additionally, Altogic's powerful integrations with popular tools like Stripe, Twilio, OneSignal, even OpenAI, enable developers to easily connect their applications with other business systems and data sources.
tip
Here is the Top 10 Features of Altogic that you can use to build your own app, without worrying about the infrastructure.
Overall, while Node.js and Express offer a robust set of tools for building applications, Altogic provides a much faster and easier approach to app development, with a user-friendly interface, pre-built nodes, and integrations, and a range of customization options.