Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Supercharge Rust APIs with Serverless Functions
Hackmamba profile imageDemola Malomo
Demola Malomo forHackmamba

Posted on

     

Supercharge Rust APIs with Serverless Functions

Serverless Function is a single-purpose programming feature that allows developers to write and deploy software without purchasing and maintaining servers. It lets developers focus on the core business logic of their application without worrying about logistics, operation, and infrastructure associated with software development.

In this post, we will learn how to use Serverless Functions to build a user management API in Rust using Netlify, Xata, and Cloudinary.

Technology overview

Netlify is a platform for deploying and building highly-performant and dynamic websites, e-commerce stores, and web applications.

Xata is an HTTP API-based serverless database for building scalable applications. It supports a built-in search engine, branching, scalability, and durability without manually managing database configuration and deployment.

Cloudinary is a visual media service for uploading, storing, managing, transforming, and delivering images and videos for websites and applications.

GitHub links

The project source codes are below:

Prerequisites

To follow along with this tutorial, the following requirements apply:

To create a database for storing our user management API’s data, we need to log into our Xata’s workspace, click theAdd a Database button, inputusers as the database name, andCreate.

Add a Database
input database name

Next, we need to create a table in our database. To do this, click theStart from scratch menu, inputuserDetails as the table name, andAdd table.

click the start from scratch
input and create

PS:Xata auto-generates an ID column (a unique identifier) for our table.

With that done, we need to add afirstName,lastName,phoneNumber, andavatar columns to our table. To do this, click on thePlus icon, selectString, input column name, andCreate column.

plus icon
select string
input name

After creating the columns, our updated table should be similar to the screenshot below:

updated table

Get Database URL and set up API Key
By default, Xata provides a unique and secure URL for accessing the database. To get our database URL, click theGet code snippet button, copy the URL, and then click theSet up API key button to generate API key.

Get code snippet

URL and Set up API key

PS:The URL we need to copy starts from the*https://……*section

Click theAdd a key button, inputxata-function as the name, andSave.

Add a key
Copy API Key

We must copy and keep the URL and generated API key, as they will come in handy when building our serverless functions.

Image sourcing and upload to Cloudinary

Next, we need to upload an image we will use as a default avatar when creating a user.

Avatar url

  • bit.ly/3gUBL7E

In our Cloudinary dashboard, we uploaded the image by clicking on theMedia Library tab, clicking onUpload, selecting theWeb Address option, inputting theURL, and clicking on theArrow Button to upload.

Cloudinary console and upload button for other formats of upload

select web address and enter url

After uploading the image, we will see it displayed on the console. To get the image URL, mouse hover on the image and click on theCopy URL icon. The URL will come in handy when building our Serverless Functions.

copy url

Creating Serverless Functions

To get started, we need to navigate to the desired directory and run the command below:

mkdirxata-functions&&cdxata-functions
Enter fullscreen modeExit fullscreen mode

The command creates a directory calledxata-functions and navigates into it.

Initializing project and installing dependencies
First, we need to initialize an empty Node.js project by running the command below:

npm init-y
Enter fullscreen modeExit fullscreen mode

Lastly, we need to installnode-fetch, a package for making HTTP requests. To do this, we need to run the command below:

npm i node-fetch
Enter fullscreen modeExit fullscreen mode

Adding logics to the Serverless Functions
With that done, we can start creating our application logic. To get started; first, we need to create anetlify folder and create afunctions folder in this folder.

updated folder structure

Secondly, we need to create acreate.js file inside thefunctions folder and add the snippet below:

importfetchfrom'node-fetch';exports.handler=asyncfunction(event,context,callback){letbodyRequest=JSON.parse(event.body);constbody={firstName:bodyRequest.firstName,lastName:bodyRequest.lastName,phoneNumber:bodyRequest.phoneNumber,avatar:'https://res.cloudinary.com/dtgbzmpca/image/upload/v1667083687/abstract-user-flat-4.png',};constresponse=awaitfetch(`${process.env.XATA_URL}:main/tables/userDetails/data`,{method:'POST',headers:{'Content-Type':'application/json',Authorization:`Bearer${process.env.XATA_API_KEY}`,},body:JSON.stringify(body),});constdata=awaitresponse.json();return{statusCode:200,body:JSON.stringify(data),};};
Enter fullscreen modeExit fullscreen mode

The snippet above does the following:

  • Imports the required dependency
  • Gets the request body
  • Uses the request body to create abody object by passing in thefirstName,lastName,phoneNumber, andavatar default URL we got from Cloudinary
  • Creates aPOST request to the Xata database by passing the URL as an environment variable with the database details and API key
  • Returns the appropriate response.

We also constructed the Xata database URL by passing in thebranch,table name, andendpoint type.

https://sample-databaseurl/users:<BRANCH NAME>/tables/<TABLE NAME>/ENDPOINT TYPE
Enter fullscreen modeExit fullscreen mode

We can get the required details from our workspace

Branch name and Table name

In our case, we filled it using an environment variable. We will add it when deploying our application to Netlify. An adequately filled URL is below:

https://sample-databaseurl/users:main/tables/userDetails/data
Enter fullscreen modeExit fullscreen mode

Thirdly, we need to create aget.js file inside the samefunctions folder and add the snippet below:

importfetchfrom'node-fetch';exports.handler=asyncfunction(){constbody={page:{size:15,},};constresponse=awaitfetch(`${process.env.XATA_URL}:main/tables/userDetails/query`,{method:'POST',headers:{'Content-Type':'application/json',Authorization:`Bearer${process.env.XATA_API_KEY}`,},body:JSON.stringify(body),});constdata=awaitresponse.json();return{statusCode:200,body:JSON.stringify(data),};};
Enter fullscreen modeExit fullscreen mode

The snippet above works similarly to thecreate.js file. However, we created abody object to paginate the requested data from the Xata database.

Lastly, we need to add a deployment file that instructs Netlify to build our application effectively. To do this, we need to create anetlify.toml file in the root directory of our project and add the snippet below:

[functions]node_bundler="esbuild"
Enter fullscreen modeExit fullscreen mode

Pushing our source code to GitHub
We need to push our source code to GitHub to enable a seamless deployment process. To get started, we need to log into our GitHub account, create a repository, inputxata-functions as the name andCreate repository.

Next, we initialize agit project and save recent changes in our project by running the command below:

git initgit add.git commit-m"add serverless functions"
Enter fullscreen modeExit fullscreen mode

To push our changes, copy and run the highlighted commands on the terminal:

Push changes to GitHub

Deploying to Netlify

To get started, we need to log into ourNetlify dashboard. Click onAdd new site dropdown and selectImport an existing project.

Create site

SelectGitHub as the Git provider and authorize Netlify.

select Github

Search forxata-function and select the repository.

select repository

Click onShow advanced, click theNew variable button and add theXATA_URL andXATA_API_KEY environment variables as key and their corresponding values.

Show advanced
input env variable

As earlier mentioned, we can get Xata’s URL and API key from our workspace.

URL
API KEY

Click onDeploy site button to start deployment. It might take a few minutes.

We can view and get our deployed Serverless Functions URL by navigating to theFunctions tab, and click on any of the functions to access the URL.

Deployed Functions
Copy Function URL

Leveraging the Serverless Functions to build Rust APIs

With our Serverless Functions up and running, we can start leveraging them to build our user management APIs. To get started, we need to navigate to the desired directory and run the command below in our terminal:

cargo new rust-user-service&&cdrust-user-service
Enter fullscreen modeExit fullscreen mode

This command creates a Rust project calledrust-user-service and navigates into the project directory.

Next, we proceed to install the required dependencies by modifying the[dependencies] section of theCargo.toml file as shown below:

//othercodesectiongoeshere[dependencies]actix-web="4"serde={version="1.0.145",features=["derive"]}serde_json="1.0"reqwest={version="0.11",features=["json"]}
Enter fullscreen modeExit fullscreen mode

actix-web = "4" is a Rust-based framework for building web applications.

serde = { version = "1.0.145", features = ["derive"] } is a framework for serializing and deserializing Rust data structures. E.g. convert Rust structs to JSON and vice versa.

serde_json = "1.0" is a crate that uses theserde crate manipulate JSON and vice versa

reqwest = { version = "0.11", features = ["json"] } is a HTTP request crate.

We need to run the command below to install the dependencies:

cargo build
Enter fullscreen modeExit fullscreen mode

Structuring our application
It is essential to have a good project structure as it makes the project maintainable and easier for us and others to read our codebase.
To do this, we need to navigate to thesrc directory and, in this folder, createapi folder. Inside theapi folder, we also need to createmod.rs,models.rs,services.rs, andhandlers.rs files.

Updated file structure

mod.rs is file for managing application visibility.

models.rs is for structuring our application data.

services.rs is for abstracting our application logics.

handlers.rs is for structuring our APIs.

Next, we need to declare these files as a module by importing them into themod.rs file

pubmodhandlers;pubmodmodels;pubmodservices;
Enter fullscreen modeExit fullscreen mode

Finally, we need to registerapi folder as a parent module by importing it into themain.rs file as shown below:

modapi;fnmain(){println!("Hello world")}
Enter fullscreen modeExit fullscreen mode

Create the API models
Next, we need to create models to represent our application data. To do this, we need to modify themodel.rs file as shown below:

useserde::{Deserialize,Serialize};#[derive(Deserialize,Serialize,Debug,Clone)]#[serde(rename_all="camelCase")]pubstructUser{pubid:Option<String>,pubfirst_name:String,publast_name:String,pubphone_number:String,pubavatar:Option<String>,}#[derive(Deserialize,Serialize,Debug,Clone)]pubstructRecords{pubrecords:Vec<User>,}#[derive(Deserialize,Serialize,Debug,Clone)]#[serde(rename_all="camelCase")]pubstructCreateResponse{pubid:String,}#[derive(Serialize,Debug,Clone)]pubstructAPIResponse<T>{pubstatus:u16,pubmessage:String,pubdata:Option<T>,}#[derive(Serialize,Debug,Clone)]pubstructAPIErrorResponse{pubstatus:u16,pubmessage:String,pubdata:Option<String>,}
Enter fullscreen modeExit fullscreen mode

The snippet above does the following:

  • Imports the required dependency
  • CreatesUser andRecords structs with required properties to describe request and response body accordingly
  • Creates aCreateResponse,APIResponse, andAPIErrorResponse structs with the required properties needed for the API response

PS: The*#[serde(rename_all = "camelCase")]*macro converts snake case properties to camel case and the*derive*macro adds implementation support for serialization, deserializations, and cloning.

Create the API services
With our Serverless Functions up and running, we can use it to create a user service. To do this, we need to add the snippet below to theservices.rs file:

usesuper::models::{CreateResponse,Records,User};usereqwest::{Client,Error};pubstructUserService{}implUserService{pubasyncfnget_user()->Result<Records,Error>{leturl="<NETLIFY FUNCTION GET URL>";letclient=Client::new();letfetched_users=client.get(url).send().await;matchfetched_users{Ok(response)=>{letjson=response.text().await?;letnew_records:Records=serde_json::from_str(json.as_str()).unwrap();Ok(new_records)}Err(error)=>Err(error),}}pubasyncfncreate_user(new_user:User)->Result<CreateResponse,Error>{leturl="<NETLIFY FUNCTION CREATE URL>";letjson_body=User{id:None,first_name:new_user.first_name,last_name:new_user.last_name,phone_number:new_user.phone_number,avatar:None,};letclient=Client::new();letfetched_users=client.post(url).json(&json_body).send().await;matchfetched_users{Ok(response)=>{letjson=response.text().await?;letcreated_record:CreateResponse=serde_json::from_str(json.as_str()).unwrap();Ok(created_record)}Err(error)=>Err(error),}}}
Enter fullscreen modeExit fullscreen mode

The snippet above does the following:

  • Imports the required dependencies
  • Creates aUserService struct
  • Creates an implementation block that adds methods to theUserService struct
  • Adds aget_user method to get the list of users by making an HTTP request to theget Serverless Function URL and returns the list of users
  • Adds acreate_user method to create a user by making an HTTP request to thecreate Serverless Function URL and returns the appropriate response

As earlier mentioned, we can get our URLs from the Netlify function tab.

Deployed Functions
Copy Function URL

Create the API handlers
With that done, we can use the services to create our API handlers. To do this, we need to add the snippet below tohandlers.rs file:

useactix_web::{get,post,web::Json,HttpResponse};usereqwest::StatusCode;usesuper::{models::{APIErrorResponse,APIResponse,CreateResponse,Records,User},services::UserService,};#[get("/users")]pubasyncfnget_user()->HttpResponse{letuser_details=UserService::get_user().await;matchuser_details{Ok(data)=>HttpResponse::Ok().json(APIResponse::<Records>{status:StatusCode::OK.as_u16(),message:"success".to_string(),data:Some(data),}),Err(error)=>HttpResponse::InternalServerError().json(APIErrorResponse{status:StatusCode::INTERNAL_SERVER_ERROR.as_u16(),message:"failure".to_string(),data:Some(error.to_string()),}),}}#[post("/users")]pubasyncfncreate_user(data:Json<User>)->HttpResponse{letnew_user=User{id:None,first_name:data.first_name.clone(),last_name:data.last_name.clone(),phone_number:data.phone_number.clone(),avatar:None,};letuser_details=UserService::create_user(new_user).await;matchuser_details{Ok(data)=>HttpResponse::Accepted().json(APIResponse::<CreateResponse>{status:StatusCode::ACCEPTED.as_u16(),message:"success".to_string(),data:Some(data),}),Err(error)=>HttpResponse::InternalServerError().json(APIErrorResponse{status:StatusCode::INTERNAL_SERVER_ERROR.as_u16(),message:"failure".to_string(),data:Some(error.to_string()),}),}}
Enter fullscreen modeExit fullscreen mode

The snippet above does the following:

  • Imports the required dependencies
  • Creates aget_user handler with a/users API route that uses theget_user service to get the list of users and returns the appropriate response using theAPIResponse andAPIErrorResponse
  • Creates acreate_user handler with a/users API route that uses thecreate_user service to create a user and returns the appropriate response using theAPIResponse andAPIErrorResponse

Putting it all together
With that done, we need to update themain.rs file to include our application entry point and use theget_user andcreate_user handlers.

useactix_web::{App,HttpServer};useapi::handlers::{create_user,get_user};modapi;#[actix_web::main]asyncfnmain()->std::io::Result<()>{HttpServer::new(move||App::new().service(get_user).service(create_user)).bind(("localhost",8080))?.run().await}
Enter fullscreen modeExit fullscreen mode

The snippet above does the following:

  • Imports the required dependencies
  • Creates a new server that adds theget_user andcreate_user handlers and runs onlocalhost:8080

With that done, we can start a development server using the command below:

cargo run main.go
Enter fullscreen modeExit fullscreen mode

get list of users
create a user

We can also verify the APIs by checking the Xata’s workspace

updated data

Conclusion

This post discussed how to quickly create user management APIs in Rust without manually deploying and managing databases and servers. With the powerful trio of Xata, Netlify, and Cloudinary, developers can build and ship applications faster by focusing on what matters and not being bothered by infrastructure bottlenecks.

These resources might be helpful:

Top comments(2)

Subscribe
pic
Create template

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

Dismiss
CollapseExpand
 
desmondsanctity profile image
Desmond Obisi
DX Software Engineer | OSS Contributor | Web3 Engineer
  • Email
  • Location
    Lagos, Nigeria
  • Education
    Federal University of Technology Owerri, Nigeria
  • Pronouns
    He/Him
  • Work
    Open to Work
  • Joined

Love this piece 🚀

CollapseExpand
 
malomz profile image
Demola Malomo
A Line of code at a time
  • Joined

🙏

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

Do you want to create product-focused no-bullshit technical content for your software product?

More fromHackmamba

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