Ever wondered how are newsletter systems made for product websites out there? Let's create our own newsletter system where people can come your website and subscribe to your newsletter system. Since we don't want a backend always running in the background it would an amazing idea to implement it with a functions. So let's use the one of the best Cloud Functions services out there, YES Appwrite Function.
I've already drafted an article where I've implemented Appwrite functions with TS.
Let's Start
Let's first create an UI for our website, this can be anything you want to have. I'm just make this so that I can hit on appwrite functions. You can directly skip if you've your UI already implemented. UI Part
Step 1: I'll be using vitejs vanilla app to get a webpage quickly. You can find this on their website to scaffold an application
yarn create vite my-vue-app --template vanilla
Step 2: Remove all the unnecessary css and js code. I remove counter.js and this is how my other files look like
main.js
Now let's add attributes, we just need the email in our case
Create an API key for your function, this is needed because your function is a basically a backend application which needs access to your database and other appwrite features like accounts, storage and more.
a. Now go to your overview tab and click on Create API key
b. Add permissions just for documents.write. Always keep your permissions to the minimal. This would help you to keep your Appwrite function more secure and also give an idea what features you're actually going to use.
c. Copy the API key we're going to use it further to add it in our function settings
Creating functions
There are many ways to create a function in appwrite but I like to use the CLI. So you can do the installation by following the installation on this link
Then go into your project. And scaffold the project with the below command
cli
appwrite init project
cli
appwrite init function ## I've used nodejs 18 for my settings
Once you've installed the project upload it to your appwrite console
cli
appwrite deploy function
Amazing we're half way through, now we will move on to actually add code to your appwrite function.
App password
So basically now that we're using Gmail as our client to send emails, we've to use it's app passwords feature to get a passwords. It's pretty intuitive, you just go to your 2factor auth -> turn it on and scroll below to add app passwords. For more information follow this article
Appwrite Functions Code
Code explaining the main driver function code please comments explaining what is happening in function, I've also added jsdocs for this function so that you can have a level of typing when you're developing the app.
Why is this important?
Well when you're prototyping your function you would want to see in your editor the obvious mistakes so that you won't have to upload multiple times to check for dumb errors which can be sorted in the editor.
Code explanation
Basically here the sendEmail() function helps us creating a connection to our GMAIL SMTP server which we would further use to send emails. We're using app password here that we generated in the above step and would keep that in our .env our further steps. And then our driver code is taking the email from our post call which in-turn first adds the email to our collection that we created in the above steps and then would call the sendEmail(). The call would be triggered from our client app but you can use the Appwrite console to test this out too!
functions/Scratch Newsletter/src/main.js
import{Client,Databases,ID}from'node-appwrite';importnodemailerfrom'nodemailer';/**
* @param {string} toEmail
* @returns
*/asyncfunctionsendEmail(toEmail,log){// Create a new transportervartransporter=nodemailer.createTransport({host:"smtp.gmail.com",port:587,secure:false,auth:{user:'mnimitsavant@gmail.com',pass:process.env.GMAIL_APP_PASSWORD,}});// Send the email objectvarmessage={from:"mnimitsavant@gmail.com",to:toEmail,subject:"test email",html:"<h1>Welcome to Scratch Newsletter</h1><br /><p>Thank you for signing up with us!</p>",};// Send the emailconstinfo=awaittransporter.sendMail(message);// Log the message idlog(`Message sent ${info.messageId}`);returninfo.messageId;}/**
* @typedef {object} Req
* @property {string} method
* @property {JSON} body
* @property {String} body.email
* @property {object} query
*//**
* @typedef {object} Res
* @property {Function} json
* @property {Function} send
* @property {Function} empty
*//**
* @param {object} context
* @param {Req} context.req
* @param {Res} context.res
* @param {object} context.log
* @param {object} context.error
*
*/exportdefaultasync ({req,res,log,error})=>{// Why not try the Appwrite SDK?//varheaders={};headers['Content-Type']='application/json';headers['Access-Control-Allow-Origin']='*';headers["Access-Control-Allow-Headers"]="Content-Type";headers['RAccess-Control-Allow-Methods']='RANDOM';log('Scratch Newsletter function invoked')try{// Create connection with Appwrite Client// This was important to add because the websites will have cors, so first they'll send a small request to check if the server is accepting the request and then the post call will happenif(req.method==='OPTIONS'){log('OPTIONS METHOD')returnres.send('',204,headers);}elseif(req.method==='POST'){log('Request method is POST')constclient=newClient().setEndpoint('https://cloud.appwrite.io/v1').setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID).setKey(process.env.APPWRITE_API_KEY);// Get the request body and parseconstbody=req.body;if(!body.email){// Send a responselog('Email is required')returnres.send('Email is required',400,headers);}// Create a new database instanceconstdatabase=newDatabases(client);// Create a new document in the databaseconstnewData=awaitdatabase.createDocument(process.env.APPWRITE_DATABASE_ID,process.env.APPWRITE_COLLECTION_ID,ID.unique(),{email:body.email});// Log the new datalog(newData);if(newData.$id){// Send new emailawaitsendEmail(newData.email,log);// Send a responsereturnres.send('Subscribed to the newsletter',201,headers);}else{// Send a responsereturnres.send('Failed to subscribe to the newsletter',409,headers);}}else{// Send a response when not using POST methodreturnres.send('Invalid request method. Please use POST method to subscribe to the newsletter.',405,headers);}}catch (err){// Log the errorerror(err.toString());returnres.send('Some error occurred. Please try again later.');}};
Add secrets to our Appwrite console
Go to out function settings and then go to environment variable section and click on editor and add your respective .env.
Edit our Client App to hit our function
Step 1.We add a function setupSubscribeButton which helps to hit our function.