{"id":5007,"date":"2022-07-18T14:27:49","date_gmt":"2022-07-18T14:27:49","guid":{"rendered":"https:\/\/codevoweb.com\/?p=5007"},"modified":"2023-05-05T20:21:05","modified_gmt":"2023-05-05T20:21:05","slug":"api-with-python-fastapi-signup-user-and-verify-email","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/api-with-python-fastapi-signup-user-and-verify-email\/","title":{"rendered":"Build API with Python &#038; FastAPI: SignUp User and Verify Email"},"content":{"rendered":"\n<p>This article will teach you how to send HTML Emails with Python, FastAPI, PyMongo, MongoDB, Jinja2, and Docker. Also, you will learn how to use Jinja2 to generate different HTML templates.<\/p>\n\n\n\n<p>API with Python, PyMongo, MongoDB, and FastAPI series:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"\/restful-api-with-python-fastapi-access-and-refresh-tokens\">RESTful API with Python &amp; FastAPI: Access and Refresh Tokens<\/a><\/li>\n\n\n\n<li><a href=\"\/restful-api-with-python-fastapi-send-html-emails\">RESTful API with Python &amp; FastAPI: Send HTML Emails<\/a><\/li>\n\n\n\n<li><a href=\"\/crud-restful-api-server-with-python-fastapi-and-mongodb\">CRUD RESTful API Server with Python, FastAPI, and MongoDB<\/a><\/li>\n<\/ol>\n\n\n\n<p>Related Articles:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/golang-grpc-server-and-client-signup-user-verify-email\">Build Golang gRPC Server and Client: SignUp User &amp; Verify Email<\/a><\/li>\n\n\n\n<li><a href=\"\/crud-api-node-js-and-postgresql-send-html-emails\">CRUD API with Node.js and PostgreSQL: Send HTML Emails<\/a><\/li>\n\n\n\n<li><a href=\"\/api-golang-mongodb-send-html-emails-gomail\">API with Golang + MongoDB: Send HTML Emails with Gomail<\/a><\/li>\n\n\n\n<li><a href=\"\/api-node-postgresql-typeorm-send-emails\">API with Node.js + PostgreSQL + TypeORM: Send Emails<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"478\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email.webp\" alt=\"Build API with Python &amp; FastAPI SignUp User and Verify Email\" class=\"wp-image-5062\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email.webp 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email-300x169.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email-768x432.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email-100x56.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Build-API-with-Python-FastAPI-SignUp-User-and-Verify-Email-700x394.webp 700w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-content-wrap{padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-contents-title-wrap{padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-content-wrap .kb-table-of-content-list{color:#ffffff;font-weight:regular;font-style:normal;margin-top:10px;margin-right:0px;margin-bottom:0px;margin-left:-5px;}@media all and (max-width: 1024px){.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}@media all and (max-width: 767px){.kb-table-of-content-nav.kb-table-of-content-id_3e0b0a-31 .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}<\/style>\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Before you start, you should:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Have a basic understanding of Python<\/li>\n\n\n\n<li>Have Python <strong>3.6+<\/strong> installed on your computer<\/li>\n\n\n\n<li>Have Docker and Docker-compose installed<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Send HTML Emails with Jinja2 &amp; FastAPI Example<\/h2>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<p>When a user registers for an account, FastAPI will validate the credentials and send a verification email to the provided email address.<\/p>\n\n\n\n<p>This authentication step is needed to ensure that users provide valid email addresses.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"863\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-863x1024.webp\" alt=\"registration form with no validation errors react hook form and zod\" class=\"wp-image-1370\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-863x1024.webp 863w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-253x300.webp 253w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-768x912.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-84x100.webp 84w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-379x450.webp 379w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod-600x712.webp 600w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/registration-form-with-no-validation-errors-react-hook-form-and-zod.webp 904w\" sizes=\"auto, (max-width: 863px) 100vw, 863px\" \/><\/figure>\n\n\n\n<p>After the FastAPI server receives the request and validates the user&#8217;s credentials, an HTML email verification template is generated and sent to the provided email.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"1001\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page.png\" alt=\"API-with-Node.js-PostgreSQL-TypeORM-email-verification-page\" class=\"wp-image-1589\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page.png 955w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page-286x300.png 286w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page-768x805.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page-95x100.png 95w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page-429x450.png 429w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verification-page-600x629.png 600w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure>\n\n\n\n<p>The user receives the notification and opens the email sent by the FastAPI server.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"765\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-1024x765.png\" alt=\"email sent by gomail golang\" class=\"wp-image-1796\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-1024x765.png 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-300x224.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-768x574.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-100x75.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-602x450.png 602w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang-600x448.png 600w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/email-sent-by-gomail-golang.png 1425w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>When the user clicks the &#8220;<strong>Verify Your Account<\/strong>&#8221; button, the user is taken to the email verification page where the verification code is pre-filled in the text input field.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"958\" height=\"1025\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code.png\" alt=\"API with Node.js + PostgreSQL + TypeORM send verification code\" class=\"wp-image-1587\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code.png 958w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code-280x300.png 280w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code-768x822.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code-93x100.png 93w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code-421x450.png 421w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-send-verification-code-600x642.png 600w\" sizes=\"auto, (max-width: 958px) 100vw, 958px\" \/><\/figure>\n\n\n\n<p>The user clicks on the &#8220;<strong>VERIFY EMAIL<\/strong>&#8221; button and the frontend app makes a <strong>GET<\/strong> request with the verification code in the URL parameters to the FastAPI server.<\/p>\n\n\n\n<p>The FastAPI server extracts and validates the verification code in the URL parameters before updating the user&#8217;s credentials to be valid in the database.<\/p>\n\n\n\n<p>The FastAPI server returns a success message to the frontend app assuming the verification code was valid.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"1013\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified.png\" alt=\"API with Node.js + PostgreSQL + TypeORM email verified\" class=\"wp-image-1586\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified.png 960w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified-284x300.png 284w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified-768x810.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified-95x100.png 95w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified-426x450.png 426w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/API-with-Node.js-PostgreSQL-TypeORM-email-verified-600x633.png 600w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the SMTP Provider Account<\/h2>\n\n\n\n<p>To avoid sending the development emails to real email addresses, we will use <a href=\"https:\/\/mailtrap.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Mailtrap<\/a> to capture them.<\/p>\n\n\n\n<p>However, before you deploy the application into production, you need to replace the SMTP credentials in the <code>.env<\/code> file. You can easily get real SMTP credentials from an SMTP service provider (<a href=\"https:\/\/trial.smtp.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">SMTP.com<\/a>, <a href=\"https:\/\/sendgrid.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">SendGrid<\/a>, <a href=\"https:\/\/www.mailgun.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Mailgun<\/a>) to send real emails to your customers. <\/p>\n\n\n\n<p>Follow these steps to register an account with <a href=\"https:\/\/mailtrap.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Mailtrap<\/a>. You can skip this section if you already have a <a href=\"https:\/\/mailtrap.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Mailtrap<\/a> account.<\/p>\n\n\n\n<p><strong>Step 1:<\/strong> Navigate to <a href=\"https:\/\/mailtrap.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Mailtrap<\/a> and provide your credentials to create a new account.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"709\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-1024x709.png\" alt=\"create mailtrap account for golang email\" class=\"wp-image-1790\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-1024x709.png 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-300x208.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-768x532.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-100x69.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-650x450.png 650w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email-600x415.png 600w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/create-mailtrap-account-for-golang-email.png 1190w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong>Step 2:<\/strong> Once the account has been created, log into it and click on the &#8220;<strong>Add Inbox<\/strong>&#8221; button. Input the inbox name and click on the &#8220;<strong>Save<\/strong>&#8221; button.<\/p>\n\n\n\n<p>Next, click on the gear icon on the newly-created inbox to display the credentials page.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"993\" height=\"680\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings.png\" alt=\"mailtrap fastapi python click settings\" class=\"wp-image-4849\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings.png 993w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings-300x205.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings-768x526.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings-100x68.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-click-settings-657x450.png 657w\" sizes=\"auto, (max-width: 993px) 100vw, 993px\" \/><\/figure>\n\n\n\n<p>Next, click on the <strong>&#8220;Show Credentials&#8221;<\/strong> dropdown link on the Settings page to display the SMTP credentials.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"875\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-1024x875.png\" alt=\"mailtrap fastapi python smtp credentials_1\" class=\"wp-image-4851\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-1024x875.png 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-300x256.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-768x656.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-100x85.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1-527x450.png 527w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/mailtrap-fastapi-python-smtp-credentials_1.png 1028w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Edit the Environment Variables File<\/h2>\n\n\n\n<p>Open the <code>.env<\/code> file and add the SMTP credentials.<\/p>\n\n\n\n<p><strong>.env<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nEMAIL_HOST=smtp.mailtrap.io\nEMAIL_PORT=587\nEMAIL_USERNAME=4aeca0c9318dd2\nEMAIL_PASSWORD=a987a0e0eac00d\nEMAIL_FROM=admin@admin.com\n<\/code>\n<\/pre>\n\n\n\n<p>After copying and pasting the SMTP credentials, you should end up with something like this:<\/p>\n\n\n\n<p><strong>.env<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nMONGO_INITDB_ROOT_USERNAME=admin\nMONGO_INITDB_ROOT_PASSWORD=password123\nMONGO_INITDB_DATABASE=fastapi\n\nDATABASE_URL=mongodb:\/\/admin:password123@localhost:6000\/fastapi?authSource=admin\n\nACCESS_TOKEN_EXPIRES_IN=15\nREFRESH_TOKEN_EXPIRES_IN=60\nJWT_ALGORITHM=RS256\n\nCLIENT_ORIGIN=http:\/\/localhost:3000\n\nEMAIL_HOST=smtp.mailtrap.io\nEMAIL_PORT=587\nEMAIL_USERNAME=90cf952fb44469\nEMAIL_PASSWORD=0524531956c552\nEMAIL_FROM=admin@admin.com\n\nJWT_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT2dJQkFBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVmlQWlJyVFpjd3l4RVhVRGpNaFZuCi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUUpBYm5LaENOQ0dOSFZGaHJPQ0RCU0IKdmZ2ckRWUzVpZXAwd2h2SGlBUEdjeWV6bjd0U2RweUZ0NEU0QTNXT3VQOXhqenNjTFZyb1pzRmVMUWlqT1JhUwp3UUloQU84MWl2b21iVGhjRkltTFZPbU16Vk52TGxWTW02WE5iS3B4bGh4TlpUTmhBaUVBbWRISlpGM3haWFE0Cm15QnNCeEhLQ3JqOTF6bVFxU0E4bHUvT1ZNTDNSak1DSVFEbDJxOUdtN0lMbS85b0EyaCtXdnZabGxZUlJPR3oKT21lV2lEclR5MUxaUVFJZ2ZGYUlaUWxMU0tkWjJvdXF4MHdwOWVEejBEWklLVzVWaSt6czdMZHRDdUVDSUVGYwo3d21VZ3pPblpzbnU1clBsTDJjZldLTGhFbWwrUVFzOCtkMFBGdXlnCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t\nJWT_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVgppUFpSclRaY3d5eEVYVURqTWhWbi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Validating the Environment Variables with Pydantic<\/h2>\n\n\n\n<p>To load the SMTP credentials we added to the <code>.env<\/code> file, we need to add the variables to the <code>app\/config.py<\/code> for the <a href=\"https:\/\/pydantic-docs.helpmanual.io\/usage\/settings\/\" target=\"_blank\" rel=\"noreferrer noopener\">Pydantic<\/a> package to load and validate them.<\/p>\n\n\n\n<p><strong>app\/config.py<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\nfrom pydantic import BaseSettings, EmailStr\n\n\nclass Settings(BaseSettings):\n    DATABASE_URL: str\n    MONGO_INITDB_DATABASE: str\n\n    JWT_PUBLIC_KEY: str\n    JWT_PRIVATE_KEY: str\n    REFRESH_TOKEN_EXPIRES_IN: int\n    ACCESS_TOKEN_EXPIRES_IN: int\n    JWT_ALGORITHM: str\n\n    CLIENT_ORIGIN: str\n\n    EMAIL_HOST: str\n    EMAIL_PORT: int\n    EMAIL_USERNAME: str\n    EMAIL_PASSWORD: str\n    EMAIL_FROM: EmailStr\n\n    class Config:\n        env_file = '.\/.env'\n\n\nsettings = Settings()\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the HTML Email Templates in FastAPI<\/h2>\n\n\n\n<p>There are different template engines in Python (<a href=\"https:\/\/pythonhosted.org\/Cheetah\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cheetah<\/a>, <a href=\"https:\/\/www.fullstackpython.com\/mako.html\" target=\"_blank\" rel=\"noreferrer noopener\">Mako<\/a>, <a href=\"https:\/\/chameleon.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noreferrer noopener\">Chameleon<\/a>, <a href=\"http:\/\/docs.diazo.org\/en\/latest\/\" target=\"_blank\" rel=\"noreferrer noopener\">Diazo<\/a>, <a href=\"https:\/\/github.com\/breily\/juno\" target=\"_blank\" rel=\"noreferrer noopener\">Juno<\/a>, and more) but we are going to use <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a> as our templating engine since it is a self-contained open-source project.<\/p>\n\n\n\n<p>Install <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a> with this command:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\npip install Jinja2\n<\/code>\n<\/pre>\n\n\n\n<p>Next, create a <code>app\/templates<\/code> folder to hold all the HTML templates.<\/p>\n\n\n\n<p>With that out of the way, let&#8217;s create a <code>app\/templates\/_styles.html<\/code> file to contain the CSS styles needed by the HTML templates.<\/p>\n\n\n\n<p><strong>app\/templates\/_styles.html<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-html\"><code>\n&lt;style&gt;\n  \/* -------------------------------------\n          GLOBAL RESETS\n      ------------------------------------- *\/\n\n  \/*All the styling goes here*\/\n\n  img {\n    border: none;\n    -ms-interpolation-mode: bicubic;\n    max-width: 100%;\n  }\n\n  body {\n    background-color: #f6f6f6;\n    font-family: sans-serif;\n    -webkit-font-smoothing: antialiased;\n    font-size: 14px;\n    line-height: 1.4;\n    margin: 0;\n    padding: 0;\n    -ms-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%;\n  }\n\n  table {\n    border-collapse: separate;\n    mso-table-lspace: 0pt;\n    mso-table-rspace: 0pt;\n    width: 100%;\n  }\n  table td {\n    font-family: sans-serif;\n    font-size: 14px;\n    vertical-align: top;\n  }\n\n  \/* -------------------------------------\n          BODY &amp; CONTAINER\n      ------------------------------------- *\/\n\n  .body {\n    background-color: #f6f6f6;\n    width: 100%;\n  }\n\n  \/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something *\/\n  .container {\n    display: block;\n    margin: 0 auto !important;\n    \/* makes it centered *\/\n    max-width: 580px;\n    padding: 10px;\n    width: 580px;\n  }\n\n  \/* This should also be a block element, so that it will fill 100% of the .container *\/\n  .content {\n    box-sizing: border-box;\n    display: block;\n    margin: 0 auto;\n    max-width: 580px;\n    padding: 10px;\n  }\n\n  \/* -------------------------------------\n          HEADER, FOOTER, MAIN\n      ------------------------------------- *\/\n  .main {\n    background: #ffffff;\n    border-radius: 3px;\n    width: 100%;\n  }\n\n  .wrapper {\n    box-sizing: border-box;\n    padding: 20px;\n  }\n\n  .content-block {\n    padding-bottom: 10px;\n    padding-top: 10px;\n  }\n\n  .footer {\n    clear: both;\n    margin-top: 10px;\n    text-align: center;\n    width: 100%;\n  }\n  .footer td,\n  .footer p,\n  .footer span,\n  .footer a {\n    color: #999999;\n    font-size: 12px;\n    text-align: center;\n  }\n\n  \/* -------------------------------------\n          TYPOGRAPHY\n      ------------------------------------- *\/\n  h1,\n  h2,\n  h3,\n  h4 {\n    color: #000000;\n    font-family: sans-serif;\n    font-weight: 400;\n    line-height: 1.4;\n    margin: 0;\n    margin-bottom: 30px;\n  }\n\n  h1 {\n    font-size: 35px;\n    font-weight: 300;\n    text-align: center;\n    text-transform: capitalize;\n  }\n\n  p,\n  ul,\n  ol {\n    font-family: sans-serif;\n    font-size: 14px;\n    font-weight: normal;\n    margin: 0;\n    margin-bottom: 15px;\n  }\n  p li,\n  ul li,\n  ol li {\n    list-style-position: inside;\n    margin-left: 5px;\n  }\n\n  a {\n    color: #3498db;\n    text-decoration: underline;\n  }\n\n  \/* -------------------------------------\n          BUTTONS\n      ------------------------------------- *\/\n  .btn {\n    box-sizing: border-box;\n    width: 100%;\n  }\n  .btn &gt; tbody &gt; tr &gt; td {\n    padding-bottom: 15px;\n  }\n  .btn table {\n    width: auto;\n  }\n  .btn table td {\n    background-color: #ffffff;\n    border-radius: 5px;\n    text-align: center;\n  }\n  .btn a {\n    background-color: #ffffff;\n    border: solid 1px #3498db;\n    border-radius: 5px;\n    box-sizing: border-box;\n    color: #3498db;\n    cursor: pointer;\n    display: inline-block;\n    font-size: 14px;\n    font-weight: bold;\n    margin: 0;\n    padding: 12px 25px;\n    text-decoration: none;\n    text-transform: capitalize;\n  }\n\n  .btn-primary table td {\n    background-color: #3498db;\n  }\n\n  .btn-primary a {\n    background-color: #3498db;\n    border-color: #3498db;\n    color: #ffffff;\n  }\n\n  \/* -------------------------------------\n          OTHER STYLES THAT MIGHT BE USEFUL\n      ------------------------------------- *\/\n  .last {\n    margin-bottom: 0;\n  }\n\n  .first {\n    margin-top: 0;\n  }\n\n  .align-center {\n    text-align: center;\n  }\n\n  .align-right {\n    text-align: right;\n  }\n\n  .align-left {\n    text-align: left;\n  }\n\n  .clear {\n    clear: both;\n  }\n\n  .mt0 {\n    margin-top: 0;\n  }\n\n  .mb0 {\n    margin-bottom: 0;\n  }\n\n  .preheader {\n    color: transparent;\n    display: none;\n    height: 0;\n    max-height: 0;\n    max-width: 0;\n    opacity: 0;\n    overflow: hidden;\n    mso-hide: all;\n    visibility: hidden;\n    width: 0;\n  }\n\n  .powered-by a {\n    text-decoration: none;\n  }\n\n  hr {\n    border: 0;\n    border-bottom: 1px solid #f6f6f6;\n    margin: 20px 0;\n  }\n\n  \/* -------------------------------------\n          RESPONSIVE AND MOBILE FRIENDLY STYLES\n      ------------------------------------- *\/\n  @media only screen and (max-width: 620px) {\n    table.body h1 {\n      font-size: 28px !important;\n      margin-bottom: 10px !important;\n    }\n    table.body p,\n    table.body ul,\n    table.body ol,\n    table.body td,\n    table.body span,\n    table.body a {\n      font-size: 16px !important;\n    }\n    table.body .wrapper,\n    table.body .article {\n      padding: 10px !important;\n    }\n    table.body .content {\n      padding: 0 !important;\n    }\n    table.body .container {\n      padding: 0 !important;\n      width: 100% !important;\n    }\n    table.body .main {\n      border-left-width: 0 !important;\n      border-radius: 0 !important;\n      border-right-width: 0 !important;\n    }\n    table.body .btn table {\n      width: 100% !important;\n    }\n    table.body .btn a {\n      width: 100% !important;\n    }\n    table.body .img-responsive {\n      height: auto !important;\n      max-width: 100% !important;\n      width: auto !important;\n    }\n  }\n\n  \/* -------------------------------------\n          PRESERVE THESE STYLES IN THE HEAD\n      ------------------------------------- *\/\n  @media all {\n    .ExternalClass {\n      width: 100%;\n    }\n    .ExternalClass,\n    .ExternalClass p,\n    .ExternalClass span,\n    .ExternalClass font,\n    .ExternalClass td,\n    .ExternalClass div {\n      line-height: 100%;\n    }\n    .apple-link a {\n      color: inherit !important;\n      font-family: inherit !important;\n      font-size: inherit !important;\n      font-weight: inherit !important;\n      line-height: inherit !important;\n      text-decoration: none !important;\n    }\n    #MessageViewBody a {\n      color: inherit;\n      text-decoration: none;\n      font-size: inherit;\n      font-family: inherit;\n      font-weight: inherit;\n      line-height: inherit;\n    }\n    .btn-primary table td:hover {\n      background-color: #34495e !important;\n    }\n    .btn-primary a:hover {\n      background-color: #34495e !important;\n      border-color: #34495e !important;\n    }\n  }\n&lt;\/style&gt;\n<\/code>\n<\/pre>\n\n\n\n<p>Now let&#8217;s create the <code>app\/templates\/base.html<\/code> that other templates can extend. With this approach, we can easily generate HTML templates like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the Email verification template<\/li>\n\n\n\n<li>the password reset template<\/li>\n\n\n\n<li>the welcome template<\/li>\n<\/ul>\n\n\n\n<p>To ensure that all the HTML templates have the CSS styles defined in <code>_styles.html<\/code> file, we need to add it to the <code>base.html<\/code> template using the <code>include<\/code>&nbsp;tag provided by <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a>.<\/p>\n\n\n\n<p>Also, we will use the <code>{{ }}<\/code> syntax to define placeholders for the variables we will be passing to the templates.<\/p>\n\n\n\n<p>With <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a>, we use <code>{%&nbsp;block&nbsp;%}<\/code> tags to define HTML skeletons that will be overridden by child templates.<\/p>\n\n\n\n<p><strong>app\/templates\/base.html<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-html\"><code>\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/&gt;\n    &lt;meta http-equiv=\"Content-Type\" content=\"text\/html; charset=UTF-8\" \/&gt;\n    &lt;title&gt;{{subject}}&lt;\/title&gt;\n    {% include '_styles.html' %}\n  &lt;\/head&gt;\n  &lt;body&gt;\n    &lt;table\n      role=\"presentation\"\n      border=\"0\"\n      cellpadding=\"0\"\n      cellspacing=\"0\"\n      class=\"body\"\n    &gt;\n      &lt;tr&gt;\n        &lt;td&gt;&amp;nbsp;&lt;\/td&gt;\n        &lt;td class=\"container\"&gt;\n          &lt;div class=\"content\"&gt;\n            &lt;!-- START CENTERED WHITE CONTAINER --&gt;\n            &lt;table role=\"presentation\" class=\"main\"&gt;\n              &lt;!-- START MAIN CONTENT AREA --&gt;\n              &lt;tr&gt;\n                &lt;td class=\"wrapper\"&gt;\n                  &lt;table\n                    role=\"presentation\"\n                    border=\"0\"\n                    cellpadding=\"0\"\n                    cellspacing=\"0\"\n                  &gt;\n                    &lt;tr&gt;\n                      &lt;td&gt;{% block content %} {% endblock %}&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                  &lt;\/table&gt;\n                &lt;\/td&gt;\n              &lt;\/tr&gt;\n\n              &lt;!-- END MAIN CONTENT AREA --&gt;\n            &lt;\/table&gt;\n            &lt;!-- END CENTERED WHITE CONTAINER --&gt;\n          &lt;\/div&gt;\n        &lt;\/td&gt;\n        &lt;td&gt;&amp;nbsp;&lt;\/td&gt;\n      &lt;\/tr&gt;\n    &lt;\/table&gt;\n  &lt;\/body&gt;\n&lt;\/html&gt;\n<\/code>\n<\/pre>\n\n\n\n<p>Since we have the <code>base.html<\/code> template defined, let&#8217;s create the child template <code>verification.html<\/code> to extend the base template.<\/p>\n\n\n\n<p>In the child template, we need to extend the base template using the <code>{%&nbsp;extends&nbsp;%}<\/code>  tag provided by <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a> and write the content between <code>{% block content %}...{% endblock %}<\/code> .<\/p>\n\n\n\n<p><strong>app\/templates\/verification.html<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-html\"><code>\n{% extends 'base.html' %} {% block content %}\n\n&lt;p&gt;Hi {{first_name}},&lt;\/p&gt;\n&lt;p&gt;\n  Thanks for creating an account with us. Please verify your email address by\n  clicking the button below.\n&lt;\/p&gt;\n&lt;table\n  role=\"presentation\"\n  border=\"0\"\n  cellpadding=\"0\"\n  cellspacing=\"0\"\n  class=\"btn btn-primary\"\n&gt;\n  &lt;tbody&gt;\n    &lt;tr&gt;\n      &lt;td align=\"left\"&gt;\n        &lt;table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"&gt;\n          &lt;tbody&gt;\n            &lt;tr&gt;\n              &lt;td&gt;\n                &lt;a href=\"{{url}}\" target=\"_blank\"&gt;Verify email address&lt;\/a&gt;\n              &lt;\/td&gt;\n            &lt;\/tr&gt;\n          &lt;\/tbody&gt;\n        &lt;\/table&gt;\n      &lt;\/td&gt;\n    &lt;\/tr&gt;\n  &lt;\/tbody&gt;\n&lt;\/table&gt;\n&lt;p&gt;Good luck! Hope it works.&lt;\/p&gt;\n\n{% endblock %}\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the SMTP Email Sender<\/h2>\n\n\n\n<p>By default, Python comes with a built-in <code>smtplib<\/code> module for sending SMTP emails but we are going to use the <a href=\"https:\/\/sabuhish.github.io\/fastapi-mail\/\" target=\"_blank\" rel=\"noreferrer noopener\">fastapi-mail<\/a> module since it has support for  <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a>.<\/p>\n\n\n\n<p>Install the <a href=\"https:\/\/sabuhish.github.io\/fastapi-mail\/\" target=\"_blank\" rel=\"noreferrer noopener\">fastapi-mail<\/a> package with this command:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\n pip install fastapi-mail\n<\/code>\n<\/pre>\n\n\n\n<p>Next, create a <code>app\/email.py<\/code> file and add the following imports:<\/p>\n\n\n\n<p><strong>app\/email.py<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\nfrom typing import List\nfrom fastapi_mail import FastMail, MessageSchema, ConnectionConfig\nfrom pydantic import EmailStr, BaseModel\nfrom .config import settings\nfrom jinja2 import Environment, select_autoescape, PackageLoader\n<\/code>\n<\/pre>\n\n\n\n<p><a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a> provides a couple of methods that we can use to load the HTML templates from a folder or a directory in the file system.<\/p>\n\n\n\n<p>The <code>PackageLoader<\/code> method is used to load HTML templates from a folder located in a Python package.<\/p>\n\n\n\n<p>To turn the directory containing the templates folder into a Python package, you will need to create an empty <code>__init__.py<\/code> file in it.<\/p>\n\n\n\n<p>Alternatively, <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a> provides the <code>FileSystemLoader()<\/code> method that we can use to load the templates from a directory in the file system.<\/p>\n\n\n\n<p><strong>app\/email.py<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\n# imports (..)\n\nenv = Environment(\n    loader=PackageLoader('app', 'templates'),\n    autoescape=select_autoescape(['html', 'xml'])\n)\n<\/code>\n<\/pre>\n\n\n\n<p>Now let&#8217;s create a utility class that will allow us to chain multiple methods to send different emails at once.<\/p>\n\n\n\n<p><strong>app\/email.py<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\nfrom typing import List\nfrom fastapi_mail import FastMail, MessageSchema, ConnectionConfig\nfrom pydantic import EmailStr, BaseModel\nfrom .config import settings\nfrom jinja2 import Environment, select_autoescape, PackageLoader\n\n\nenv = Environment(\n    loader=PackageLoader('app', 'templates'),\n    autoescape=select_autoescape(['html', 'xml'])\n)\n\n\nclass EmailSchema(BaseModel):\n    email: List[EmailStr]\n\n\nclass Email:\n    def __init__(self, user: dict, url: str, email: List[EmailStr]):\n        self.name = user['name']\n        self.sender = 'Codevo &lt;admin@admin.com&gt;'\n        self.email = email\n        self.url = url\n        pass\n\n    async def sendMail(self, subject, template):\n        # Define the config\n        conf = ConnectionConfig(\n            MAIL_USERNAME=settings.EMAIL_USERNAME,\n            MAIL_PASSWORD=settings.EMAIL_PASSWORD,\n            MAIL_FROM=settings.EMAIL_FROM,\n            MAIL_PORT=settings.EMAIL_PORT,\n            MAIL_SERVER=settings.EMAIL_HOST,\n            MAIL_STARTTLS=False,\n            MAIL_SSL_TLS=False,\n            USE_CREDENTIALS=True,\n            VALIDATE_CERTS=True\n        )\n        # Generate the HTML template base on the template name\n        template = env.get_template(f'{template}.html')\n\n        html = template.render(\n            url=self.url,\n            first_name=self.name,\n            subject=subject\n        )\n\n        # Define the message options\n        message = MessageSchema(\n            subject=subject,\n            recipients=self.email,\n            body=html,\n            subtype=\"html\"\n        )\n\n        # Send the email\n        fm = FastMail(conf)\n        await fm.send_message(message)\n\n    async def sendVerificationCode(self):\n        await self.sendMail('Your verification code (Valid for 10min)', 'verification')\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Sending the HTML Emails in FastAPI<\/h2>\n\n\n\n<p>To begin, let&#8217;s generate a random <strong>10-byte<\/strong> string that we will use as the verification code and hash it using the <code>sha256<\/code> algorithm.<\/p>\n\n\n\n<p>After hashing the string, we will save it in the database and send the unhashed one to the user&#8217;s email.<\/p>\n\n\n\n<p>Now let&#8217;s construct the URL and evoke the <code>sendVerificationCode()<\/code> method we defined in the <code>Email<\/code> class to send the email.<\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\n try:\n        token = randbytes(10)\n        hashedCode = hashlib.sha256()\n        hashedCode.update(token)\n        verification_code = hashedCode.hexdigest()\n        User.find_one_and_update({\"_id\": result.inserted_id}, {\n            \"$set\": {\"verification_code\": verification_code, \"updated_at\": datetime.utcnow()}})\n\n        url = f\"{request.url.scheme}:\/\/{request.client.host}:{request.url.port}\/api\/auth\/verifyemail\/{token.hex()}\"\n        await Email(userEntity(new_user), url, [EmailStr(payload.email)]).sendVerificationCode()\n    except Exception as error:\n        User.find_one_and_update({\"_id\": result.inserted_id}, {\n            \"$set\": {\"verification_code\": None, \"updated_at\": datetime.utcnow()}})\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,\n                            detail='There was an error sending email')\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Update the SignUp Controller<\/h2>\n\n\n\n<p>Now let&#8217;s update the <code>create_user()<\/code> operation function to send the verification email immediately after the user has been successfully added to the database. <\/p>\n\n\n\n<p><strong>app\/routers\/auth.py<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\n@router.post('\/register', status_code=status.HTTP_201_CREATED)\nasync def create_user(payload: schemas.CreateUserSchema, request: Request):\n    # Check if user already exist\n    user = User.find_one({'email': payload.email.lower()})\n    if user:\n        raise HTTPException(status_code=status.HTTP_409_CONFLICT,\n                            detail='Account already exist')\n    # Compare password and passwordConfirm\n    if payload.password != payload.passwordConfirm:\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST, detail='Passwords do not match')\n    #  Hash the password\n    payload.password = utils.hash_password(payload.password)\n    del payload.passwordConfirm\n    payload.role = 'user'\n    payload.verified = False\n    payload.email = payload.email.lower()\n    payload.created_at = datetime.utcnow()\n    payload.updated_at = payload.created_at\n\n    result = User.insert_one(payload.dict())\n    new_user = User.find_one({'_id': result.inserted_id})\n    try:\n        token = randbytes(10)\n        hashedCode = hashlib.sha256()\n        hashedCode.update(token)\n        verification_code = hashedCode.hexdigest()\n        User.find_one_and_update({\"_id\": result.inserted_id}, {\n            \"$set\": {\"verification_code\": verification_code, \"updated_at\": datetime.utcnow()}})\n\n        url = f\"{request.url.scheme}:\/\/{request.client.host}:{request.url.port}\/api\/auth\/verifyemail\/{token.hex()}\"\n        await Email(userEntity(new_user), url, [EmailStr(payload.email)]).sendVerificationCode()\n    except Exception as error:\n        User.find_one_and_update({\"_id\": result.inserted_id}, {\n            \"$set\": {\"verification_code\": None, \"updated_at\": datetime.utcnow()}})\n        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,\n                            detail='There was an error sending email')\n    return {'status': 'success', 'message': 'Verification token successfully sent to your email'}\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create a Handler to Validate the Verification Code<\/h2>\n\n\n\n<p>Finally, let&#8217;s create a router to validate the verification code and update the user&#8217;s credentials to be valid in the database.<\/p>\n\n\n\n<pre class=\"line-numbers language-py\"><code>\n@router.get('\/verifyemail\/{token}')\ndef verify_me(token: str):\n    hashedCode = hashlib.sha256()\n    hashedCode.update(bytes.fromhex(token))\n    verification_code = hashedCode.hexdigest()\n    result = User.find_one_and_update({\"verification_code\": verification_code}, {\n        \"$set\": {\"verification_code\": None, \"verified\": True, \"updated_at\": datetime.utcnow()}}, new=True)\n    if not result:\n        raise HTTPException(\n            status_code=status.HTTP_403_FORBIDDEN, detail='Invalid verification code or account already verified')\n    return {\n        \"status\": \"success\",\n        \"message\": \"Account verified successfully\"\n    }\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>With this <a href=\"https:\/\/www.fullstackpython.com\/jinja2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Jinja2<\/a>, FastAPI, PyMongo, MongoDB, <a href=\"https:\/\/sabuhish.github.io\/fastapi-mail\/\" target=\"_blank\" rel=\"noreferrer noopener\">FastApi-MAIL<\/a>, and Docker-compose example, you&#8217;ve learned how to send HTML Emails in Python.<\/p>\n\n\n\n<p>Check out the source codes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/wpcodevo\/fastapi_mongodb\/tree\/fastapi_mongodb_verify_email\" target=\"_blank\" rel=\"noreferrer noopener\">Python MongoDB API Send HTML Emails<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/wpcodevo\/react-mui-frontend.git\" target=\"_blank\" rel=\"noreferrer noopener\">React frontend app<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This article will teach you how to send HTML Emails with Python, FastAPI, PyMongo, MongoDB, Jinja2, and Docker. Also, you will learn how to use&#8230;<\/p>\n","protected":false},"author":1,"featured_media":5062,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[20],"tags":[25,31],"class_list":["post-5007","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-python","tag-python-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/5007","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/comments?post=5007"}],"version-history":[{"count":2,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/5007\/revisions"}],"predecessor-version":[{"id":11267,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/5007\/revisions\/11267"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/5062"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=5007"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=5007"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=5007"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}