Build your own serverless API
Introduction
In this tutorial, you will learn how to build a serverless API using Morty. You will create a simple API that returns a list of users. You will also learn how to deploy your API to the cloud.
Prerequisites
- Morty cli (opens in a new tab) (>= 1.0.0)
- Morty instance
Create a new project
To create a new project, run the following command:
morty function init -r node-19 serverless-api
This will create a new project in the serverless-api
directory. The -r
flag specifies the runtime to use. In this case, we will use the node-19
runtime.
Create our function
Update the serverless-api/handler.js
file with the following content:
First, we will create an in-memory database to store our users. We will use an array of users for this example. Then, we will create a set of functions to interact with our database. We will create functions to get all users, get a user by id, create a new user, update a user and delete a user. Finally, we will export our functions as a module. This will allow us to use our functions in our function handler.
Database
For this example, we will use a simple array of users as our database. Of course, this is not a good idea for a production application but it will be enough for this example. Database will be reinitialized every time the function is instantiated. This is because the function is stateless and will be destroyed after some time of inactivity.
// In-memory database
// Only for demo purposes, do not use in production
let users = [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Alicia Lacoupe" },
{ id: 3, name: "Josh Dupont" }
];
Functions
To get all users, we will create a function that returns the users
array.
const getAllUsers = () => {
return users;
};
To get a user by id, we will create a function that returns the user with the specified id.
const getUserById = (id) => {
return users.find((user) => user.id == id);
};
To create a new user, we will create a function that adds the user to the users
array and returns the newly created user.
const createUser = (user) => {
users.push(user);
return user;
};
Same logic can be used to update a user. We will create a function that updates the user with the specified id and returns the updated user.
const updateUser = (id, user) => {
const index = users.findIndex((user) => user.id == id);
users[index] = { ...users[index], ...user };
return users[index];
};
Finally, to delete a user, we will create a function that removes the user with the specified id from the users
array and returns the updated array.
const deleteUser = (id) => {
users = users.filter((user) => user.id != id);
return users;
};
Finally, we will export our functions as a module. This will allow us to use our functions in our function handler.
// Export our functions as a module
exports.handler = async function (req, res) {
// Get the request method, query parameters and body (req is an express request object)
const { method, query, body } = req;
switch (method) {
case "GET":
const response = query.userId ? getUserById(query.userId) : getAllUsers();
return res.status(200).send(JSON.stringify(response));
case "POST":
if (!body || body === "" || body === "{}") {
return res.status(400).send("Missing body");
}
const user = createUser(JSON.parse(body));
return res.status(200).send(JSON.stringify(user));
case "PUT":
if (!query.userId) {
return res.status(400).send("Missing userId");
}
if (!body) {
return res.status(400).send("Missing body");
}
const updatedUser = updateUser(query.userId, body);
return res.status(200).send(JSON.stringify(updatedUser));
case "DELETE":
if (!query.userId) {
return res.status(400).send("Missing userId");
}
const isDeleted = deleteUser(query.userId);
return res.status(200).send(JSON.stringify(isDeleted));
default:
return res.status(405).end(`Method ${method} Not Allowed`);
}
};
Final code
Here is the final code for our function:
// In-memory database
// Only for demo purposes, do not use in production
let users = [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Alicia Lacoupe" },
{ id: 3, name: "Josh Dupont" }
];
const getAllUsers = () => {
return users;
};
const getUserById = (id) => {
return users.find((user) => user.id == id);
};
const createUser = (user) => {
users.push(user);
return user;
};
const updateUser = (id, user) => {
const index = users.findIndex((user) => user.id == id);
users[index] = { ...users[index], ...user };
return users[index];
};
const deleteUser = (id) => {
users = users.filter((user) => user.id != id);
return users;
};
// Export our functions as a module
exports.handler = async function (req, res) {
// Get the request method, query parameters and body (req is an express request object)
const { method, query, body } = req;
switch (method) {
case "GET":
const response = query.userId ? getUserById(query.userId) : getAllUsers();
return res.status(200).send(JSON.stringify(response));
case "POST":
if (!body || body === "" || body === "{}") {
return res.status(400).send("Missing body");
}
const user = createUser(JSON.parse(body));
return res.status(200).send(JSON.stringify(user));
case "PUT":
if (!query.userId) {
return res.status(400).send("Missing userId");
}
if (!body) {
return res.status(400).send("Missing body");
}
const updatedUser = updateUser(query.userId, body);
return res.status(200).send(JSON.stringify(updatedUser));
case "DELETE":
if (!query.userId) {
return res.status(400).send("Missing userId");
}
const isDeleted = deleteUser(query.userId);
return res.status(200).send(JSON.stringify(isDeleted));
default:
return res.status(405).end(`Method ${method} Not Allowed`);
}
};
Deploy our function
To deploy our function, run the following command:
morty function build ./serverless-api
Test our function
To test our function, run the following command:
Get all users
morty function invoke serverless-api -X GET
Response :
[
{
"id": 1,
"name": "John Doe"
},
{
"id": 2,
"name": "Alicia Lacoupe"
},
{
"id": 3,
"name": "Josh Dupont"
}
]
Create a new user
morty function invoke serverless-api -X POST -d "{ id: 4, name: 'Jack Sparrow' }" -H "Content-Type: application/json"
Response :
{
"id":4,
"name":"Jack Sparrow"
}
Update a user
morty function invoke serverless-api1 -X PUT -p userId=2 -d "{ name: 'Serge Blanco' }" -H "Content-Type: application/json"
Response :
{
"id":2,
"name":"Serge Blanco"
}
Get a user by id
morty function invoke serverless-api1 -X GET -p userId=2
Response :
{
"id":2,
"name":"Serge Blanco"
}
Delete a user
morty function invoke serverless-api1 -X DELETE -p userId=2
Response :
[
{
"id": 1,
"name": "John Doe"
},
{
"id": 3,
"name": "Josh Dupont"
}
]