[{"data":1,"prerenderedAt":3953},["ShallowReactive",2],{"blog:2023:send-email-with-nuxt3-cloudflare-and-brevo":3,"blogMore-Development":3939,"comments-send-email-with-nuxt3-cloudflare-and-brevo":3952},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"category":11,"tags":12,"excerpt":17,"body":44,"_type":3928,"_id":3929,"_source":3930,"_file":3931,"_stem":3932,"_extension":3933,"url":3934,"wordCount":3935,"minutes":392,"commentCount":3936,"image":3937},"/blog/2023/send-email-with-nuxt3-cloudflare-and-brevo","2023",false,"en","Email form sender with Nuxt3, Cloudflare, Brevo & reCAPTCHA","I've been using Nuxt quite extensively on the static sites I work on and host and use Cloudflare Pages to host them. It's a great combination of power, flexibility, performance and cost (free).","2023-05-31T10:03:00-08:00","Development",[13,14,15,16],"Nuxt","Cloudflare","Brevo","webdev",{"type":18,"children":19},"root",[20,35],{"type":21,"tag":22,"props":23,"children":24},"element","p",{},[25,28,29,31,33],{"type":26,"value":27},"text","I've been using ",{"type":26,"value":13},{"type":26,"value":30}," quite extensively on the static sites I work on and host and use ",{"type":26,"value":32},"Cloudflare Pages",{"type":26,"value":34}," to host them. It's a great combination of power, flexibility, performance and cost (free).",{"type":21,"tag":22,"props":36,"children":37},{},[38,40,42],{"type":26,"value":39},"While one of the sites I manage uses ",{"type":26,"value":41},"ActiveCampaign",{"type":26,"value":43}," successfully for both their newsletters and contact forms, this latest customer just wanted plain emails ideally with no subscription involved.",{"type":18,"children":45,"toc":3916},[46,66,77,82,89,99,104,135,140,154,958,963,969,983,988,1009,1014,1275,1288,1722,1735,1757,1763,1777,1782,1796,1803,1816,1843,1855,2107,2112,2743,2749,2754,3253,3258,3584,3590,3595,3643,3686,3691,3835,3862,3868,3873,3878,3884,3889,3894,3899,3905,3910],{"type":21,"tag":22,"props":47,"children":48},{},[49,50,58,59,65],{"type":26,"value":27},{"type":21,"tag":51,"props":52,"children":56},"a",{"href":53,"rel":54},"https://nuxt.com/",[55],"nofollow",[57],{"type":26,"value":13},{"type":26,"value":30},{"type":21,"tag":51,"props":60,"children":63},{"href":61,"rel":62},"https://pages.cloudflare.com/",[55],[64],{"type":26,"value":32},{"type":26,"value":34},{"type":21,"tag":22,"props":67,"children":68},{},[69,70,76],{"type":26,"value":39},{"type":21,"tag":51,"props":71,"children":74},{"href":72,"rel":73},"https://www.activecampaign.com/",[55],[75],{"type":26,"value":41},{"type":26,"value":43},{"type":21,"tag":22,"props":78,"children":79},{},[80],{"type":26,"value":81},"Thus started my journey to find a simple, free, email service that I could use to send emails from a Nuxt3 site hosted on Cloudflare Pages.",{"type":21,"tag":83,"props":84,"children":86},"h2",{"id":85},"brevo-email-sender",[87],{"type":26,"value":88},"Brevo email sender",{"type":21,"tag":90,"props":91,"children":98},"img",{"src":92,"alt":93,"width":94,"className":95,"style":97},"https://img.damieng.com/blog/brevo-dashboard.webp","Brevo dashboard",480,[96],"screenshot","float: right",[],{"type":21,"tag":22,"props":100,"children":101},{},[102],{"type":26,"value":103},"What I needed was an email service that I could POST to that would then send the email on to the customers email address with the message from the contact form as well as a reply-to address corresponding to the contact information from the form.",{"type":21,"tag":22,"props":105,"children":106},{},[107,109,115,117,124,126,133],{"type":26,"value":108},"I found ",{"type":21,"tag":51,"props":110,"children":113},{"href":111,"rel":112},"https://fas.st/t/wsP61jZT",[55],[114],{"type":26,"value":15},{"type":26,"value":116}," (referral link) which provides a ",{"type":21,"tag":51,"props":118,"children":121},{"href":119,"rel":120},"https://developers.brevo.com/reference/sendtransacemail",[55],[122],{"type":26,"value":123},"Transactional Email",{"type":26,"value":125}," feature that did exactly what I wanted and their ",{"type":21,"tag":51,"props":127,"children":130},{"href":128,"rel":129},"https://www.brevo.com/pricing/",[55],[131],{"type":26,"value":132},"free tier has 300 emails/day",{"type":26,"value":134}," which is more than enough for their contact form. As a bonus it includes some really great logging and statistics to keep an eye on things and make development and testing a little easier.",{"type":21,"tag":22,"props":136,"children":137},{},[138],{"type":26,"value":139},"The developer docs guide you through using their SDK and while I normally go that route this was such a simple use case that I decided to just use their HTTP API directly and avoid bringing in dependencies especially given this is going to be called from a serverless function (more on that in a moment).",{"type":21,"tag":22,"props":141,"children":142},{},[143,145,152],{"type":26,"value":144},"Once you've signed up you need to head over to ",{"type":21,"tag":51,"props":146,"children":149},{"href":147,"rel":148},"https://app.brevo.com/settings/keys/api",[55],[150],{"type":26,"value":151},"get a v3 API key",{"type":26,"value":153}," which you will need to send in the headers when making the API request.",{"type":21,"tag":155,"props":156,"children":161},"pre",{"className":157,"code":158,"language":159,"meta":160,"style":160},"language-typescript shiki shiki-themes everforest-light dracula","function sendEmail(\n  fromEmail: string,\n  firstName: string,\n  lastName: string,\n  message: string\n): string {\n  const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n    method: \"POST\",\n    headers: {\n      accept: \"application/json\",\n      \"api-key\": \"[your brevo api key here]\",\n      \"content-type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      sender: {\n        name: `[from name]`,\n        email: \"[from email address]\",\n      },\n      to: [{ email: \"[to name]\", name: \"[to email address]\" }],\n      replyTo: {\n        email: fromEmail,\n        name: `${firstName} ${lastName}`,\n      },\n      subject: \"Web Contact Form\",\n      textContent: message,\n    }),\n  })\n\n  if (!response.ok) {\n    return \"Message could not be sent at this time.\"\n  }\n}\n","typescript","",[162],{"type":21,"tag":163,"props":164,"children":165},"code",{"__ignoreMap":160},[166,190,217,238,259,277,299,359,390,407,437,476,513,522,557,574,596,626,635,706,723,740,796,804,834,852,861,870,880,917,940,949],{"type":21,"tag":167,"props":168,"children":171},"span",{"class":169,"line":170},"line",1,[172,178,184],{"type":21,"tag":167,"props":173,"children":175},{"style":174},"--shiki-default:#F85552;--shiki-dark:#FF79C6",[176],{"type":26,"value":177},"function",{"type":21,"tag":167,"props":179,"children":181},{"style":180},"--shiki-default:#8DA101;--shiki-dark:#50FA7B",[182],{"type":26,"value":183}," sendEmail",{"type":21,"tag":167,"props":185,"children":187},{"style":186},"--shiki-default:#5C6A72;--shiki-dark:#F8F8F2",[188],{"type":26,"value":189},"(\n",{"type":21,"tag":167,"props":191,"children":193},{"class":169,"line":192},2,[194,200,206,212],{"type":21,"tag":167,"props":195,"children":197},{"style":196},"--shiki-default:#5C6A72;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic",[198],{"type":26,"value":199},"  fromEmail",{"type":21,"tag":167,"props":201,"children":203},{"style":202},"--shiki-default:#939F91;--shiki-dark:#FF79C6",[204],{"type":26,"value":205},":",{"type":21,"tag":167,"props":207,"children":209},{"style":208},"--shiki-default:#3A94C5;--shiki-default-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic",[210],{"type":26,"value":211}," string",{"type":21,"tag":167,"props":213,"children":214},{"style":186},[215],{"type":26,"value":216},",\n",{"type":21,"tag":167,"props":218,"children":220},{"class":169,"line":219},3,[221,226,230,234],{"type":21,"tag":167,"props":222,"children":223},{"style":196},[224],{"type":26,"value":225},"  firstName",{"type":21,"tag":167,"props":227,"children":228},{"style":202},[229],{"type":26,"value":205},{"type":21,"tag":167,"props":231,"children":232},{"style":208},[233],{"type":26,"value":211},{"type":21,"tag":167,"props":235,"children":236},{"style":186},[237],{"type":26,"value":216},{"type":21,"tag":167,"props":239,"children":241},{"class":169,"line":240},4,[242,247,251,255],{"type":21,"tag":167,"props":243,"children":244},{"style":196},[245],{"type":26,"value":246},"  lastName",{"type":21,"tag":167,"props":248,"children":249},{"style":202},[250],{"type":26,"value":205},{"type":21,"tag":167,"props":252,"children":253},{"style":208},[254],{"type":26,"value":211},{"type":21,"tag":167,"props":256,"children":257},{"style":186},[258],{"type":26,"value":216},{"type":21,"tag":167,"props":260,"children":262},{"class":169,"line":261},5,[263,268,272],{"type":21,"tag":167,"props":264,"children":265},{"style":196},[266],{"type":26,"value":267},"  message",{"type":21,"tag":167,"props":269,"children":270},{"style":202},[271],{"type":26,"value":205},{"type":21,"tag":167,"props":273,"children":274},{"style":208},[275],{"type":26,"value":276}," string\n",{"type":21,"tag":167,"props":278,"children":280},{"class":169,"line":279},6,[281,286,290,294],{"type":21,"tag":167,"props":282,"children":283},{"style":186},[284],{"type":26,"value":285},")",{"type":21,"tag":167,"props":287,"children":288},{"style":202},[289],{"type":26,"value":205},{"type":21,"tag":167,"props":291,"children":292},{"style":208},[293],{"type":26,"value":211},{"type":21,"tag":167,"props":295,"children":296},{"style":186},[297],{"type":26,"value":298}," {\n",{"type":21,"tag":167,"props":300,"children":302},{"class":169,"line":301},7,[303,309,314,319,324,329,334,340,346,350,355],{"type":21,"tag":167,"props":304,"children":306},{"style":305},"--shiki-default:#F57D26;--shiki-dark:#FF79C6",[307],{"type":26,"value":308},"  const",{"type":21,"tag":167,"props":310,"children":311},{"style":186},[312],{"type":26,"value":313}," response",{"type":21,"tag":167,"props":315,"children":316},{"style":305},[317],{"type":26,"value":318}," =",{"type":21,"tag":167,"props":320,"children":321},{"style":174},[322],{"type":26,"value":323}," await",{"type":21,"tag":167,"props":325,"children":326},{"style":180},[327],{"type":26,"value":328}," fetch",{"type":21,"tag":167,"props":330,"children":331},{"style":186},[332],{"type":26,"value":333},"(",{"type":21,"tag":167,"props":335,"children":337},{"style":336},"--shiki-default:#DFA000;--shiki-dark:#E9F284",[338],{"type":26,"value":339},"\"",{"type":21,"tag":167,"props":341,"children":343},{"style":342},"--shiki-default:#DFA000;--shiki-dark:#F1FA8C",[344],{"type":26,"value":345},"https://api.brevo.com/v3/smtp/email",{"type":21,"tag":167,"props":347,"children":348},{"style":336},[349],{"type":26,"value":339},{"type":21,"tag":167,"props":351,"children":352},{"style":186},[353],{"type":26,"value":354},",",{"type":21,"tag":167,"props":356,"children":357},{"style":186},[358],{"type":26,"value":298},{"type":21,"tag":167,"props":360,"children":362},{"class":169,"line":361},8,[363,368,372,377,382,386],{"type":21,"tag":167,"props":364,"children":365},{"style":186},[366],{"type":26,"value":367},"    method",{"type":21,"tag":167,"props":369,"children":370},{"style":202},[371],{"type":26,"value":205},{"type":21,"tag":167,"props":373,"children":374},{"style":336},[375],{"type":26,"value":376}," \"",{"type":21,"tag":167,"props":378,"children":379},{"style":342},[380],{"type":26,"value":381},"POST",{"type":21,"tag":167,"props":383,"children":384},{"style":336},[385],{"type":26,"value":339},{"type":21,"tag":167,"props":387,"children":388},{"style":186},[389],{"type":26,"value":216},{"type":21,"tag":167,"props":391,"children":393},{"class":169,"line":392},9,[394,399,403],{"type":21,"tag":167,"props":395,"children":396},{"style":186},[397],{"type":26,"value":398},"    headers",{"type":21,"tag":167,"props":400,"children":401},{"style":202},[402],{"type":26,"value":205},{"type":21,"tag":167,"props":404,"children":405},{"style":186},[406],{"type":26,"value":298},{"type":21,"tag":167,"props":408,"children":410},{"class":169,"line":409},10,[411,416,420,424,429,433],{"type":21,"tag":167,"props":412,"children":413},{"style":186},[414],{"type":26,"value":415},"      accept",{"type":21,"tag":167,"props":417,"children":418},{"style":202},[419],{"type":26,"value":205},{"type":21,"tag":167,"props":421,"children":422},{"style":336},[423],{"type":26,"value":376},{"type":21,"tag":167,"props":425,"children":426},{"style":342},[427],{"type":26,"value":428},"application/json",{"type":21,"tag":167,"props":430,"children":431},{"style":336},[432],{"type":26,"value":339},{"type":21,"tag":167,"props":434,"children":435},{"style":186},[436],{"type":26,"value":216},{"type":21,"tag":167,"props":438,"children":440},{"class":169,"line":439},11,[441,446,451,455,459,463,468,472],{"type":21,"tag":167,"props":442,"children":443},{"style":336},[444],{"type":26,"value":445},"      \"",{"type":21,"tag":167,"props":447,"children":448},{"style":342},[449],{"type":26,"value":450},"api-key",{"type":21,"tag":167,"props":452,"children":453},{"style":336},[454],{"type":26,"value":339},{"type":21,"tag":167,"props":456,"children":457},{"style":202},[458],{"type":26,"value":205},{"type":21,"tag":167,"props":460,"children":461},{"style":336},[462],{"type":26,"value":376},{"type":21,"tag":167,"props":464,"children":465},{"style":342},[466],{"type":26,"value":467},"[your brevo api key here]",{"type":21,"tag":167,"props":469,"children":470},{"style":336},[471],{"type":26,"value":339},{"type":21,"tag":167,"props":473,"children":474},{"style":186},[475],{"type":26,"value":216},{"type":21,"tag":167,"props":477,"children":479},{"class":169,"line":478},12,[480,484,489,493,497,501,505,509],{"type":21,"tag":167,"props":481,"children":482},{"style":336},[483],{"type":26,"value":445},{"type":21,"tag":167,"props":485,"children":486},{"style":342},[487],{"type":26,"value":488},"content-type",{"type":21,"tag":167,"props":490,"children":491},{"style":336},[492],{"type":26,"value":339},{"type":21,"tag":167,"props":494,"children":495},{"style":202},[496],{"type":26,"value":205},{"type":21,"tag":167,"props":498,"children":499},{"style":336},[500],{"type":26,"value":376},{"type":21,"tag":167,"props":502,"children":503},{"style":342},[504],{"type":26,"value":428},{"type":21,"tag":167,"props":506,"children":507},{"style":336},[508],{"type":26,"value":339},{"type":21,"tag":167,"props":510,"children":511},{"style":186},[512],{"type":26,"value":216},{"type":21,"tag":167,"props":514,"children":516},{"class":169,"line":515},13,[517],{"type":21,"tag":167,"props":518,"children":519},{"style":186},[520],{"type":26,"value":521},"    },\n",{"type":21,"tag":167,"props":523,"children":525},{"class":169,"line":524},14,[526,531,535,541,547,552],{"type":21,"tag":167,"props":527,"children":528},{"style":186},[529],{"type":26,"value":530},"    body",{"type":21,"tag":167,"props":532,"children":533},{"style":202},[534],{"type":26,"value":205},{"type":21,"tag":167,"props":536,"children":538},{"style":537},"--shiki-default:#5C6A72;--shiki-dark:#BD93F9",[539],{"type":26,"value":540}," JSON",{"type":21,"tag":167,"props":542,"children":544},{"style":543},"--shiki-default:#939F91;--shiki-dark:#F8F8F2",[545],{"type":26,"value":546},".",{"type":21,"tag":167,"props":548,"children":549},{"style":180},[550],{"type":26,"value":551},"stringify",{"type":21,"tag":167,"props":553,"children":554},{"style":186},[555],{"type":26,"value":556},"({\n",{"type":21,"tag":167,"props":558,"children":560},{"class":169,"line":559},15,[561,566,570],{"type":21,"tag":167,"props":562,"children":563},{"style":186},[564],{"type":26,"value":565},"      sender",{"type":21,"tag":167,"props":567,"children":568},{"style":202},[569],{"type":26,"value":205},{"type":21,"tag":167,"props":571,"children":572},{"style":186},[573],{"type":26,"value":298},{"type":21,"tag":167,"props":575,"children":577},{"class":169,"line":576},16,[578,583,587,592],{"type":21,"tag":167,"props":579,"children":580},{"style":186},[581],{"type":26,"value":582},"        name",{"type":21,"tag":167,"props":584,"children":585},{"style":202},[586],{"type":26,"value":205},{"type":21,"tag":167,"props":588,"children":589},{"style":342},[590],{"type":26,"value":591}," `[from name]`",{"type":21,"tag":167,"props":593,"children":594},{"style":186},[595],{"type":26,"value":216},{"type":21,"tag":167,"props":597,"children":599},{"class":169,"line":598},17,[600,605,609,613,618,622],{"type":21,"tag":167,"props":601,"children":602},{"style":186},[603],{"type":26,"value":604},"        email",{"type":21,"tag":167,"props":606,"children":607},{"style":202},[608],{"type":26,"value":205},{"type":21,"tag":167,"props":610,"children":611},{"style":336},[612],{"type":26,"value":376},{"type":21,"tag":167,"props":614,"children":615},{"style":342},[616],{"type":26,"value":617},"[from email address]",{"type":21,"tag":167,"props":619,"children":620},{"style":336},[621],{"type":26,"value":339},{"type":21,"tag":167,"props":623,"children":624},{"style":186},[625],{"type":26,"value":216},{"type":21,"tag":167,"props":627,"children":629},{"class":169,"line":628},18,[630],{"type":21,"tag":167,"props":631,"children":632},{"style":186},[633],{"type":26,"value":634},"      },\n",{"type":21,"tag":167,"props":636,"children":638},{"class":169,"line":637},19,[639,644,648,653,658,662,666,671,675,679,684,688,692,697,701],{"type":21,"tag":167,"props":640,"children":641},{"style":186},[642],{"type":26,"value":643},"      to",{"type":21,"tag":167,"props":645,"children":646},{"style":202},[647],{"type":26,"value":205},{"type":21,"tag":167,"props":649,"children":650},{"style":186},[651],{"type":26,"value":652}," [{",{"type":21,"tag":167,"props":654,"children":655},{"style":186},[656],{"type":26,"value":657}," email",{"type":21,"tag":167,"props":659,"children":660},{"style":202},[661],{"type":26,"value":205},{"type":21,"tag":167,"props":663,"children":664},{"style":336},[665],{"type":26,"value":376},{"type":21,"tag":167,"props":667,"children":668},{"style":342},[669],{"type":26,"value":670},"[to name]",{"type":21,"tag":167,"props":672,"children":673},{"style":336},[674],{"type":26,"value":339},{"type":21,"tag":167,"props":676,"children":677},{"style":186},[678],{"type":26,"value":354},{"type":21,"tag":167,"props":680,"children":681},{"style":186},[682],{"type":26,"value":683}," name",{"type":21,"tag":167,"props":685,"children":686},{"style":202},[687],{"type":26,"value":205},{"type":21,"tag":167,"props":689,"children":690},{"style":336},[691],{"type":26,"value":376},{"type":21,"tag":167,"props":693,"children":694},{"style":342},[695],{"type":26,"value":696},"[to email address]",{"type":21,"tag":167,"props":698,"children":699},{"style":336},[700],{"type":26,"value":339},{"type":21,"tag":167,"props":702,"children":703},{"style":186},[704],{"type":26,"value":705}," }],\n",{"type":21,"tag":167,"props":707,"children":709},{"class":169,"line":708},20,[710,715,719],{"type":21,"tag":167,"props":711,"children":712},{"style":186},[713],{"type":26,"value":714},"      replyTo",{"type":21,"tag":167,"props":716,"children":717},{"style":202},[718],{"type":26,"value":205},{"type":21,"tag":167,"props":720,"children":721},{"style":186},[722],{"type":26,"value":298},{"type":21,"tag":167,"props":724,"children":726},{"class":169,"line":725},21,[727,731,735],{"type":21,"tag":167,"props":728,"children":729},{"style":186},[730],{"type":26,"value":604},{"type":21,"tag":167,"props":732,"children":733},{"style":202},[734],{"type":26,"value":205},{"type":21,"tag":167,"props":736,"children":737},{"style":186},[738],{"type":26,"value":739}," fromEmail,\n",{"type":21,"tag":167,"props":741,"children":743},{"class":169,"line":742},22,[744,748,752,757,763,768,773,778,783,787,792],{"type":21,"tag":167,"props":745,"children":746},{"style":186},[747],{"type":26,"value":582},{"type":21,"tag":167,"props":749,"children":750},{"style":202},[751],{"type":26,"value":205},{"type":21,"tag":167,"props":753,"children":754},{"style":342},[755],{"type":26,"value":756}," `",{"type":21,"tag":167,"props":758,"children":760},{"style":759},"--shiki-default:#8DA101;--shiki-dark:#FF79C6",[761],{"type":26,"value":762},"${",{"type":21,"tag":167,"props":764,"children":765},{"style":186},[766],{"type":26,"value":767},"firstName",{"type":21,"tag":167,"props":769,"children":770},{"style":759},[771],{"type":26,"value":772},"}",{"type":21,"tag":167,"props":774,"children":775},{"style":759},[776],{"type":26,"value":777}," ${",{"type":21,"tag":167,"props":779,"children":780},{"style":186},[781],{"type":26,"value":782},"lastName",{"type":21,"tag":167,"props":784,"children":785},{"style":759},[786],{"type":26,"value":772},{"type":21,"tag":167,"props":788,"children":789},{"style":342},[790],{"type":26,"value":791},"`",{"type":21,"tag":167,"props":793,"children":794},{"style":186},[795],{"type":26,"value":216},{"type":21,"tag":167,"props":797,"children":799},{"class":169,"line":798},23,[800],{"type":21,"tag":167,"props":801,"children":802},{"style":186},[803],{"type":26,"value":634},{"type":21,"tag":167,"props":805,"children":807},{"class":169,"line":806},24,[808,813,817,821,826,830],{"type":21,"tag":167,"props":809,"children":810},{"style":186},[811],{"type":26,"value":812},"      subject",{"type":21,"tag":167,"props":814,"children":815},{"style":202},[816],{"type":26,"value":205},{"type":21,"tag":167,"props":818,"children":819},{"style":336},[820],{"type":26,"value":376},{"type":21,"tag":167,"props":822,"children":823},{"style":342},[824],{"type":26,"value":825},"Web Contact Form",{"type":21,"tag":167,"props":827,"children":828},{"style":336},[829],{"type":26,"value":339},{"type":21,"tag":167,"props":831,"children":832},{"style":186},[833],{"type":26,"value":216},{"type":21,"tag":167,"props":835,"children":837},{"class":169,"line":836},25,[838,843,847],{"type":21,"tag":167,"props":839,"children":840},{"style":186},[841],{"type":26,"value":842},"      textContent",{"type":21,"tag":167,"props":844,"children":845},{"style":202},[846],{"type":26,"value":205},{"type":21,"tag":167,"props":848,"children":849},{"style":186},[850],{"type":26,"value":851}," message,\n",{"type":21,"tag":167,"props":853,"children":855},{"class":169,"line":854},26,[856],{"type":21,"tag":167,"props":857,"children":858},{"style":186},[859],{"type":26,"value":860},"    }),\n",{"type":21,"tag":167,"props":862,"children":864},{"class":169,"line":863},27,[865],{"type":21,"tag":167,"props":866,"children":867},{"style":186},[868],{"type":26,"value":869},"  })\n",{"type":21,"tag":167,"props":871,"children":873},{"class":169,"line":872},28,[874],{"type":21,"tag":167,"props":875,"children":877},{"emptyLinePlaceholder":876},true,[878],{"type":26,"value":879},"\n",{"type":21,"tag":167,"props":881,"children":883},{"class":169,"line":882},29,[884,889,894,899,904,908,913],{"type":21,"tag":167,"props":885,"children":886},{"style":174},[887],{"type":26,"value":888},"  if",{"type":21,"tag":167,"props":890,"children":891},{"style":186},[892],{"type":26,"value":893}," (",{"type":21,"tag":167,"props":895,"children":896},{"style":305},[897],{"type":26,"value":898},"!",{"type":21,"tag":167,"props":900,"children":901},{"style":186},[902],{"type":26,"value":903},"response",{"type":21,"tag":167,"props":905,"children":906},{"style":543},[907],{"type":26,"value":546},{"type":21,"tag":167,"props":909,"children":910},{"style":186},[911],{"type":26,"value":912},"ok)",{"type":21,"tag":167,"props":914,"children":915},{"style":186},[916],{"type":26,"value":298},{"type":21,"tag":167,"props":918,"children":920},{"class":169,"line":919},30,[921,926,930,935],{"type":21,"tag":167,"props":922,"children":923},{"style":174},[924],{"type":26,"value":925},"    return",{"type":21,"tag":167,"props":927,"children":928},{"style":336},[929],{"type":26,"value":376},{"type":21,"tag":167,"props":931,"children":932},{"style":342},[933],{"type":26,"value":934},"Message could not be sent at this time.",{"type":21,"tag":167,"props":936,"children":937},{"style":336},[938],{"type":26,"value":939},"\"\n",{"type":21,"tag":167,"props":941,"children":943},{"class":169,"line":942},31,[944],{"type":21,"tag":167,"props":945,"children":946},{"style":186},[947],{"type":26,"value":948},"  }\n",{"type":21,"tag":167,"props":950,"children":952},{"class":169,"line":951},32,[953],{"type":21,"tag":167,"props":954,"children":955},{"style":186},[956],{"type":26,"value":957},"}\n",{"type":21,"tag":22,"props":959,"children":960},{},[961],{"type":26,"value":962},"Okay, so that part is done but we need to call this from somewhere...",{"type":21,"tag":83,"props":964,"children":966},{"id":965},"creating-a-nuxt3-server-api",[967],{"type":26,"value":968},"Creating a Nuxt3 server API",{"type":21,"tag":22,"props":970,"children":971},{},[972,974,981],{"type":26,"value":973},"I thought about putting a serverless function up on my usual spots - Azure Functions or AWS Lambda - but given the site is already on Cloudflare Pages and they now support ",{"type":21,"tag":51,"props":975,"children":978},{"href":976,"rel":977},"https://developers.cloudflare.com/pages/platform/using-cloudflare-workers",[55],[979],{"type":26,"value":980},"Workers",{"type":26,"value":982}," it would be nice to keep it together.",{"type":21,"tag":22,"props":984,"children":985},{},[986],{"type":26,"value":987},"Given that the rest of the site is already a static site on Nuxt3 and this infrastructure should include server-side support let's see what we can do.",{"type":21,"tag":22,"props":989,"children":990},{},[991,993,999,1001,1007],{"type":26,"value":992},"First off we create a new file called ",{"type":21,"tag":163,"props":994,"children":996},{"className":995},[],[997],{"type":26,"value":998},"sendContactForm.post.ts",{"type":26,"value":1000}," and put it into the ",{"type":21,"tag":163,"props":1002,"children":1004},{"className":1003},[],[1005],{"type":26,"value":1006},"server/api",{"type":26,"value":1008}," folder in the Nuxt3 project. This tells Nuxt3 we want a function and that it should be on the server and that it is post only.",{"type":21,"tag":22,"props":1010,"children":1011},{},[1012],{"type":26,"value":1013},"A simple API to call our new function would look something like...",{"type":21,"tag":155,"props":1015,"children":1017},{"className":157,"code":1016,"language":159,"meta":160,"style":160},"export default defineEventHandler(async (event) => {\n  // Validate parameters\n  const { name, email, message } = await readBody(event)\n  if (!name || !email || !message) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Missing required fields\",\n    })\n  }\n\n  return sendEmail(email, name, message)\n})\n",[1018],{"type":21,"tag":163,"props":1019,"children":1020},{"__ignoreMap":160},[1021,1072,1081,1112,1160,1177,1199,1228,1236,1243,1250,1267],{"type":21,"tag":167,"props":1022,"children":1023},{"class":169,"line":170},[1024,1030,1035,1040,1044,1049,1053,1058,1063,1068],{"type":21,"tag":167,"props":1025,"children":1027},{"style":1026},"--shiki-default:#DF69BA;--shiki-dark:#FF79C6",[1028],{"type":26,"value":1029},"export",{"type":21,"tag":167,"props":1031,"children":1032},{"style":174},[1033],{"type":26,"value":1034}," default",{"type":21,"tag":167,"props":1036,"children":1037},{"style":180},[1038],{"type":26,"value":1039}," defineEventHandler",{"type":21,"tag":167,"props":1041,"children":1042},{"style":186},[1043],{"type":26,"value":333},{"type":21,"tag":167,"props":1045,"children":1046},{"style":305},[1047],{"type":26,"value":1048},"async",{"type":21,"tag":167,"props":1050,"children":1051},{"style":186},[1052],{"type":26,"value":893},{"type":21,"tag":167,"props":1054,"children":1055},{"style":196},[1056],{"type":26,"value":1057},"event",{"type":21,"tag":167,"props":1059,"children":1060},{"style":186},[1061],{"type":26,"value":1062},") ",{"type":21,"tag":167,"props":1064,"children":1065},{"style":305},[1066],{"type":26,"value":1067},"=>",{"type":21,"tag":167,"props":1069,"children":1070},{"style":186},[1071],{"type":26,"value":298},{"type":21,"tag":167,"props":1073,"children":1074},{"class":169,"line":192},[1075],{"type":21,"tag":167,"props":1076,"children":1078},{"style":1077},"--shiki-default:#939F91;--shiki-default-font-style:italic;--shiki-dark:#6272A4;--shiki-dark-font-style:inherit",[1079],{"type":26,"value":1080},"  // Validate parameters\n",{"type":21,"tag":167,"props":1082,"children":1083},{"class":169,"line":219},[1084,1088,1093,1098,1102,1107],{"type":21,"tag":167,"props":1085,"children":1086},{"style":305},[1087],{"type":26,"value":308},{"type":21,"tag":167,"props":1089,"children":1090},{"style":186},[1091],{"type":26,"value":1092}," { name, email, message } ",{"type":21,"tag":167,"props":1094,"children":1095},{"style":305},[1096],{"type":26,"value":1097},"=",{"type":21,"tag":167,"props":1099,"children":1100},{"style":174},[1101],{"type":26,"value":323},{"type":21,"tag":167,"props":1103,"children":1104},{"style":180},[1105],{"type":26,"value":1106}," readBody",{"type":21,"tag":167,"props":1108,"children":1109},{"style":186},[1110],{"type":26,"value":1111},"(event)\n",{"type":21,"tag":167,"props":1113,"children":1114},{"class":169,"line":240},[1115,1119,1123,1127,1132,1137,1142,1147,1151,1155],{"type":21,"tag":167,"props":1116,"children":1117},{"style":174},[1118],{"type":26,"value":888},{"type":21,"tag":167,"props":1120,"children":1121},{"style":186},[1122],{"type":26,"value":893},{"type":21,"tag":167,"props":1124,"children":1125},{"style":305},[1126],{"type":26,"value":898},{"type":21,"tag":167,"props":1128,"children":1129},{"style":186},[1130],{"type":26,"value":1131},"name ",{"type":21,"tag":167,"props":1133,"children":1134},{"style":305},[1135],{"type":26,"value":1136},"||",{"type":21,"tag":167,"props":1138,"children":1139},{"style":305},[1140],{"type":26,"value":1141}," !",{"type":21,"tag":167,"props":1143,"children":1144},{"style":186},[1145],{"type":26,"value":1146},"email ",{"type":21,"tag":167,"props":1148,"children":1149},{"style":305},[1150],{"type":26,"value":1136},{"type":21,"tag":167,"props":1152,"children":1153},{"style":305},[1154],{"type":26,"value":1141},{"type":21,"tag":167,"props":1156,"children":1157},{"style":186},[1158],{"type":26,"value":1159},"message) {\n",{"type":21,"tag":167,"props":1161,"children":1162},{"class":169,"line":261},[1163,1168,1173],{"type":21,"tag":167,"props":1164,"children":1165},{"style":174},[1166],{"type":26,"value":1167},"    throw",{"type":21,"tag":167,"props":1169,"children":1170},{"style":180},[1171],{"type":26,"value":1172}," createError",{"type":21,"tag":167,"props":1174,"children":1175},{"style":186},[1176],{"type":26,"value":556},{"type":21,"tag":167,"props":1178,"children":1179},{"class":169,"line":279},[1180,1185,1189,1195],{"type":21,"tag":167,"props":1181,"children":1182},{"style":186},[1183],{"type":26,"value":1184},"      statusCode",{"type":21,"tag":167,"props":1186,"children":1187},{"style":202},[1188],{"type":26,"value":205},{"type":21,"tag":167,"props":1190,"children":1192},{"style":1191},"--shiki-default:#DF69BA;--shiki-dark:#BD93F9",[1193],{"type":26,"value":1194}," 400",{"type":21,"tag":167,"props":1196,"children":1197},{"style":186},[1198],{"type":26,"value":216},{"type":21,"tag":167,"props":1200,"children":1201},{"class":169,"line":301},[1202,1207,1211,1215,1220,1224],{"type":21,"tag":167,"props":1203,"children":1204},{"style":186},[1205],{"type":26,"value":1206},"      statusMessage",{"type":21,"tag":167,"props":1208,"children":1209},{"style":202},[1210],{"type":26,"value":205},{"type":21,"tag":167,"props":1212,"children":1213},{"style":336},[1214],{"type":26,"value":376},{"type":21,"tag":167,"props":1216,"children":1217},{"style":342},[1218],{"type":26,"value":1219},"Missing required fields",{"type":21,"tag":167,"props":1221,"children":1222},{"style":336},[1223],{"type":26,"value":339},{"type":21,"tag":167,"props":1225,"children":1226},{"style":186},[1227],{"type":26,"value":216},{"type":21,"tag":167,"props":1229,"children":1230},{"class":169,"line":361},[1231],{"type":21,"tag":167,"props":1232,"children":1233},{"style":186},[1234],{"type":26,"value":1235},"    })\n",{"type":21,"tag":167,"props":1237,"children":1238},{"class":169,"line":392},[1239],{"type":21,"tag":167,"props":1240,"children":1241},{"style":186},[1242],{"type":26,"value":948},{"type":21,"tag":167,"props":1244,"children":1245},{"class":169,"line":409},[1246],{"type":21,"tag":167,"props":1247,"children":1248},{"emptyLinePlaceholder":876},[1249],{"type":26,"value":879},{"type":21,"tag":167,"props":1251,"children":1252},{"class":169,"line":439},[1253,1258,1262],{"type":21,"tag":167,"props":1254,"children":1255},{"style":174},[1256],{"type":26,"value":1257},"  return",{"type":21,"tag":167,"props":1259,"children":1260},{"style":180},[1261],{"type":26,"value":183},{"type":21,"tag":167,"props":1263,"children":1264},{"style":186},[1265],{"type":26,"value":1266},"(email, name, message)\n",{"type":21,"tag":167,"props":1268,"children":1269},{"class":169,"line":478},[1270],{"type":21,"tag":167,"props":1271,"children":1272},{"style":186},[1273],{"type":26,"value":1274},"})\n",{"type":21,"tag":22,"props":1276,"children":1277},{},[1278,1280,1286],{"type":26,"value":1279},"This is a simple function that validates the parameters and then calls our ",{"type":21,"tag":163,"props":1281,"children":1283},{"className":1282},[],[1284],{"type":26,"value":1285},"sendEmail",{"type":26,"value":1287}," function from earlier. To call it from our contact form we'd do something like this:",{"type":21,"tag":155,"props":1289,"children":1291},{"className":157,"code":1290,"language":159,"meta":160,"style":160},"// Add this to the \u003Cscript setup lang=\"ts\"> section of your form and wire up fields and button and add client validation.\n\nconst email = ref(\"\")\nconst name = ref(\"\")\nconst message = ref(\"\")\n\nconst result = await fetch(\"/api/sendContactForm\", {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    email: email.value,\n    name: name.value,\n    message: message.value,\n  }),\n})\n\nconst status = await result.text()\n//...\n",[1292],{"type":21,"tag":163,"props":1293,"children":1294},{"__ignoreMap":160},[1295,1303,1310,1346,1378,1410,1417,1463,1491,1507,1544,1552,1580,1605,1629,1654,1662,1669,1676,1714],{"type":21,"tag":167,"props":1296,"children":1297},{"class":169,"line":170},[1298],{"type":21,"tag":167,"props":1299,"children":1300},{"style":1077},[1301],{"type":26,"value":1302},"// Add this to the \u003Cscript setup lang=\"ts\"> section of your form and wire up fields and button and add client validation.\n",{"type":21,"tag":167,"props":1304,"children":1305},{"class":169,"line":192},[1306],{"type":21,"tag":167,"props":1307,"children":1308},{"emptyLinePlaceholder":876},[1309],{"type":26,"value":879},{"type":21,"tag":167,"props":1311,"children":1312},{"class":169,"line":219},[1313,1318,1323,1327,1332,1336,1341],{"type":21,"tag":167,"props":1314,"children":1315},{"style":305},[1316],{"type":26,"value":1317},"const",{"type":21,"tag":167,"props":1319,"children":1320},{"style":186},[1321],{"type":26,"value":1322}," email ",{"type":21,"tag":167,"props":1324,"children":1325},{"style":305},[1326],{"type":26,"value":1097},{"type":21,"tag":167,"props":1328,"children":1329},{"style":180},[1330],{"type":26,"value":1331}," ref",{"type":21,"tag":167,"props":1333,"children":1334},{"style":186},[1335],{"type":26,"value":333},{"type":21,"tag":167,"props":1337,"children":1338},{"style":336},[1339],{"type":26,"value":1340},"\"\"",{"type":21,"tag":167,"props":1342,"children":1343},{"style":186},[1344],{"type":26,"value":1345},")\n",{"type":21,"tag":167,"props":1347,"children":1348},{"class":169,"line":240},[1349,1353,1358,1362,1366,1370,1374],{"type":21,"tag":167,"props":1350,"children":1351},{"style":305},[1352],{"type":26,"value":1317},{"type":21,"tag":167,"props":1354,"children":1355},{"style":186},[1356],{"type":26,"value":1357}," name ",{"type":21,"tag":167,"props":1359,"children":1360},{"style":305},[1361],{"type":26,"value":1097},{"type":21,"tag":167,"props":1363,"children":1364},{"style":180},[1365],{"type":26,"value":1331},{"type":21,"tag":167,"props":1367,"children":1368},{"style":186},[1369],{"type":26,"value":333},{"type":21,"tag":167,"props":1371,"children":1372},{"style":336},[1373],{"type":26,"value":1340},{"type":21,"tag":167,"props":1375,"children":1376},{"style":186},[1377],{"type":26,"value":1345},{"type":21,"tag":167,"props":1379,"children":1380},{"class":169,"line":261},[1381,1385,1390,1394,1398,1402,1406],{"type":21,"tag":167,"props":1382,"children":1383},{"style":305},[1384],{"type":26,"value":1317},{"type":21,"tag":167,"props":1386,"children":1387},{"style":186},[1388],{"type":26,"value":1389}," message ",{"type":21,"tag":167,"props":1391,"children":1392},{"style":305},[1393],{"type":26,"value":1097},{"type":21,"tag":167,"props":1395,"children":1396},{"style":180},[1397],{"type":26,"value":1331},{"type":21,"tag":167,"props":1399,"children":1400},{"style":186},[1401],{"type":26,"value":333},{"type":21,"tag":167,"props":1403,"children":1404},{"style":336},[1405],{"type":26,"value":1340},{"type":21,"tag":167,"props":1407,"children":1408},{"style":186},[1409],{"type":26,"value":1345},{"type":21,"tag":167,"props":1411,"children":1412},{"class":169,"line":279},[1413],{"type":21,"tag":167,"props":1414,"children":1415},{"emptyLinePlaceholder":876},[1416],{"type":26,"value":879},{"type":21,"tag":167,"props":1418,"children":1419},{"class":169,"line":301},[1420,1424,1429,1433,1437,1441,1445,1449,1454,1458],{"type":21,"tag":167,"props":1421,"children":1422},{"style":305},[1423],{"type":26,"value":1317},{"type":21,"tag":167,"props":1425,"children":1426},{"style":186},[1427],{"type":26,"value":1428}," result ",{"type":21,"tag":167,"props":1430,"children":1431},{"style":305},[1432],{"type":26,"value":1097},{"type":21,"tag":167,"props":1434,"children":1435},{"style":174},[1436],{"type":26,"value":323},{"type":21,"tag":167,"props":1438,"children":1439},{"style":180},[1440],{"type":26,"value":328},{"type":21,"tag":167,"props":1442,"children":1443},{"style":186},[1444],{"type":26,"value":333},{"type":21,"tag":167,"props":1446,"children":1447},{"style":336},[1448],{"type":26,"value":339},{"type":21,"tag":167,"props":1450,"children":1451},{"style":342},[1452],{"type":26,"value":1453},"/api/sendContactForm",{"type":21,"tag":167,"props":1455,"children":1456},{"style":336},[1457],{"type":26,"value":339},{"type":21,"tag":167,"props":1459,"children":1460},{"style":186},[1461],{"type":26,"value":1462},", {\n",{"type":21,"tag":167,"props":1464,"children":1465},{"class":169,"line":361},[1466,1471,1475,1479,1483,1487],{"type":21,"tag":167,"props":1467,"children":1468},{"style":186},[1469],{"type":26,"value":1470},"  method",{"type":21,"tag":167,"props":1472,"children":1473},{"style":202},[1474],{"type":26,"value":205},{"type":21,"tag":167,"props":1476,"children":1477},{"style":336},[1478],{"type":26,"value":376},{"type":21,"tag":167,"props":1480,"children":1481},{"style":342},[1482],{"type":26,"value":381},{"type":21,"tag":167,"props":1484,"children":1485},{"style":336},[1486],{"type":26,"value":339},{"type":21,"tag":167,"props":1488,"children":1489},{"style":186},[1490],{"type":26,"value":216},{"type":21,"tag":167,"props":1492,"children":1493},{"class":169,"line":392},[1494,1499,1503],{"type":21,"tag":167,"props":1495,"children":1496},{"style":186},[1497],{"type":26,"value":1498},"  headers",{"type":21,"tag":167,"props":1500,"children":1501},{"style":202},[1502],{"type":26,"value":205},{"type":21,"tag":167,"props":1504,"children":1505},{"style":186},[1506],{"type":26,"value":298},{"type":21,"tag":167,"props":1508,"children":1509},{"class":169,"line":409},[1510,1515,1520,1524,1528,1532,1536,1540],{"type":21,"tag":167,"props":1511,"children":1512},{"style":336},[1513],{"type":26,"value":1514},"    \"",{"type":21,"tag":167,"props":1516,"children":1517},{"style":342},[1518],{"type":26,"value":1519},"Content-Type",{"type":21,"tag":167,"props":1521,"children":1522},{"style":336},[1523],{"type":26,"value":339},{"type":21,"tag":167,"props":1525,"children":1526},{"style":202},[1527],{"type":26,"value":205},{"type":21,"tag":167,"props":1529,"children":1530},{"style":336},[1531],{"type":26,"value":376},{"type":21,"tag":167,"props":1533,"children":1534},{"style":342},[1535],{"type":26,"value":428},{"type":21,"tag":167,"props":1537,"children":1538},{"style":336},[1539],{"type":26,"value":339},{"type":21,"tag":167,"props":1541,"children":1542},{"style":186},[1543],{"type":26,"value":216},{"type":21,"tag":167,"props":1545,"children":1546},{"class":169,"line":439},[1547],{"type":21,"tag":167,"props":1548,"children":1549},{"style":186},[1550],{"type":26,"value":1551},"  },\n",{"type":21,"tag":167,"props":1553,"children":1554},{"class":169,"line":478},[1555,1560,1564,1568,1572,1576],{"type":21,"tag":167,"props":1556,"children":1557},{"style":186},[1558],{"type":26,"value":1559},"  body",{"type":21,"tag":167,"props":1561,"children":1562},{"style":202},[1563],{"type":26,"value":205},{"type":21,"tag":167,"props":1565,"children":1566},{"style":537},[1567],{"type":26,"value":540},{"type":21,"tag":167,"props":1569,"children":1570},{"style":543},[1571],{"type":26,"value":546},{"type":21,"tag":167,"props":1573,"children":1574},{"style":180},[1575],{"type":26,"value":551},{"type":21,"tag":167,"props":1577,"children":1578},{"style":186},[1579],{"type":26,"value":556},{"type":21,"tag":167,"props":1581,"children":1582},{"class":169,"line":515},[1583,1588,1592,1596,1600],{"type":21,"tag":167,"props":1584,"children":1585},{"style":186},[1586],{"type":26,"value":1587},"    email",{"type":21,"tag":167,"props":1589,"children":1590},{"style":202},[1591],{"type":26,"value":205},{"type":21,"tag":167,"props":1593,"children":1594},{"style":186},[1595],{"type":26,"value":657},{"type":21,"tag":167,"props":1597,"children":1598},{"style":543},[1599],{"type":26,"value":546},{"type":21,"tag":167,"props":1601,"children":1602},{"style":186},[1603],{"type":26,"value":1604},"value,\n",{"type":21,"tag":167,"props":1606,"children":1607},{"class":169,"line":524},[1608,1613,1617,1621,1625],{"type":21,"tag":167,"props":1609,"children":1610},{"style":186},[1611],{"type":26,"value":1612},"    name",{"type":21,"tag":167,"props":1614,"children":1615},{"style":202},[1616],{"type":26,"value":205},{"type":21,"tag":167,"props":1618,"children":1619},{"style":186},[1620],{"type":26,"value":683},{"type":21,"tag":167,"props":1622,"children":1623},{"style":543},[1624],{"type":26,"value":546},{"type":21,"tag":167,"props":1626,"children":1627},{"style":186},[1628],{"type":26,"value":1604},{"type":21,"tag":167,"props":1630,"children":1631},{"class":169,"line":559},[1632,1637,1641,1646,1650],{"type":21,"tag":167,"props":1633,"children":1634},{"style":186},[1635],{"type":26,"value":1636},"    message",{"type":21,"tag":167,"props":1638,"children":1639},{"style":202},[1640],{"type":26,"value":205},{"type":21,"tag":167,"props":1642,"children":1643},{"style":186},[1644],{"type":26,"value":1645}," message",{"type":21,"tag":167,"props":1647,"children":1648},{"style":543},[1649],{"type":26,"value":546},{"type":21,"tag":167,"props":1651,"children":1652},{"style":186},[1653],{"type":26,"value":1604},{"type":21,"tag":167,"props":1655,"children":1656},{"class":169,"line":576},[1657],{"type":21,"tag":167,"props":1658,"children":1659},{"style":186},[1660],{"type":26,"value":1661},"  }),\n",{"type":21,"tag":167,"props":1663,"children":1664},{"class":169,"line":598},[1665],{"type":21,"tag":167,"props":1666,"children":1667},{"style":186},[1668],{"type":26,"value":1274},{"type":21,"tag":167,"props":1670,"children":1671},{"class":169,"line":628},[1672],{"type":21,"tag":167,"props":1673,"children":1674},{"emptyLinePlaceholder":876},[1675],{"type":26,"value":879},{"type":21,"tag":167,"props":1677,"children":1678},{"class":169,"line":637},[1679,1683,1688,1692,1696,1701,1705,1709],{"type":21,"tag":167,"props":1680,"children":1681},{"style":305},[1682],{"type":26,"value":1317},{"type":21,"tag":167,"props":1684,"children":1685},{"style":186},[1686],{"type":26,"value":1687}," status ",{"type":21,"tag":167,"props":1689,"children":1690},{"style":305},[1691],{"type":26,"value":1097},{"type":21,"tag":167,"props":1693,"children":1694},{"style":174},[1695],{"type":26,"value":323},{"type":21,"tag":167,"props":1697,"children":1698},{"style":186},[1699],{"type":26,"value":1700}," result",{"type":21,"tag":167,"props":1702,"children":1703},{"style":543},[1704],{"type":26,"value":546},{"type":21,"tag":167,"props":1706,"children":1707},{"style":180},[1708],{"type":26,"value":26},{"type":21,"tag":167,"props":1710,"children":1711},{"style":186},[1712],{"type":26,"value":1713},"()\n",{"type":21,"tag":167,"props":1715,"children":1716},{"class":169,"line":708},[1717],{"type":21,"tag":167,"props":1718,"children":1719},{"style":1077},[1720],{"type":26,"value":1721},"//...\n",{"type":21,"tag":22,"props":1723,"children":1724},{},[1725,1727,1733],{"type":26,"value":1726},"You can bind the text boxes using ",{"type":21,"tag":163,"props":1728,"children":1730},{"className":1729},[],[1731],{"type":26,"value":1732},"v-model",{"type":26,"value":1734}," and add some validation to make sure they are filled in correctly before submitting the form.",{"type":21,"tag":22,"props":1736,"children":1737},{},[1738,1740,1746,1748,1755],{"type":26,"value":1739},"Now starting local development with ",{"type":21,"tag":163,"props":1741,"children":1743},{"className":1742},[],[1744],{"type":26,"value":1745},"yarn dev",{"type":26,"value":1747}," we can submit the form and see the email come through. (If it doesn't then check the console log for errors or Brevo's great ",{"type":21,"tag":51,"props":1749,"children":1752},{"href":1750,"rel":1751},"https://app-smtp.brevo.com/log",[55],[1753],{"type":26,"value":1754},"logs",{"type":26,"value":1756}," page to see what happened).",{"type":21,"tag":83,"props":1758,"children":1760},{"id":1759},"recaptcha-to-prevent-spam",[1761],{"type":26,"value":1762},"reCAPTCHA to prevent spam",{"type":21,"tag":22,"props":1764,"children":1765},{},[1766,1768,1775],{"type":26,"value":1767},"Now that we have a working contact form we need to prevent spam. I've used ",{"type":21,"tag":51,"props":1769,"children":1772},{"href":1770,"rel":1771},"https://www.google.com/recaptcha/about/",[55],[1773],{"type":26,"value":1774},"reCAPTCHA",{"type":26,"value":1776}," before and it works great. There are two basic parts - the page executes some JS from Google that generates a token that is tied to what they think about the users behavior and likely spammyness.",{"type":21,"tag":22,"props":1778,"children":1779},{},[1780],{"type":26,"value":1781},"We then take that token and sent it to our API which calls Google with the token and our secret key and Google tells us the score. Literally with v3 we get a score between 0 and 1. If it's less than 0.5 then it's probably spam and we can reject it.",{"type":21,"tag":22,"props":1783,"children":1784},{},[1785,1787,1794],{"type":26,"value":1786},"To get going you first sign up with ",{"type":21,"tag":51,"props":1788,"children":1791},{"href":1789,"rel":1790},"https://www.google.com/recaptcha/admin/",[55],[1792],{"type":26,"value":1793},"Google reCAPTCHA",{"type":26,"value":1795}," and create a new \"site\". You'll need the site key and secret key for the next steps. You can add \"localhost\" to the domains for local testing.",{"type":21,"tag":1797,"props":1798,"children":1800},"h3",{"id":1799},"client-side-detection",[1801],{"type":26,"value":1802},"Client-side detection",{"type":21,"tag":22,"props":1804,"children":1805},{},[1806,1808,1814],{"type":26,"value":1807},"Now we need to add the reCAPTCHA script to our site. The easiest way is to add the ",{"type":21,"tag":163,"props":1809,"children":1811},{"className":1810},[],[1812],{"type":26,"value":1813},"vue-recaptcha-v3",{"type":26,"value":1815}," package to our project.",{"type":21,"tag":155,"props":1817,"children":1821},{"className":1818,"code":1819,"language":1820,"meta":160,"style":160},"language-bash shiki shiki-themes everforest-light dracula","yarn add vue-recaptcha-v3\n","bash",[1822],{"type":21,"tag":163,"props":1823,"children":1824},{"__ignoreMap":160},[1825],{"type":21,"tag":167,"props":1826,"children":1827},{"class":169,"line":170},[1828,1833,1838],{"type":21,"tag":167,"props":1829,"children":1830},{"style":180},[1831],{"type":26,"value":1832},"yarn",{"type":21,"tag":167,"props":1834,"children":1835},{"style":342},[1836],{"type":26,"value":1837}," add",{"type":21,"tag":167,"props":1839,"children":1840},{"style":342},[1841],{"type":26,"value":1842}," vue-recaptcha-v3\n",{"type":21,"tag":22,"props":1844,"children":1845},{},[1846,1848,1854],{"type":26,"value":1847},"Then we need create a new plugin in ",{"type":21,"tag":163,"props":1849,"children":1851},{"className":1850},[],[1852],{"type":26,"value":1853},"plugins/google-recaptcha.ts",{"type":26,"value":205},{"type":21,"tag":155,"props":1856,"children":1858},{"className":157,"code":1857,"language":159,"meta":160,"style":160},"import { VueReCaptcha } from \"vue-recaptcha-v3\"\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.use(VueReCaptcha, {\n    siteKey: \"[your site key here]\",\n    loaderOptions: {\n      autoHideBadge: true,\n      explicitRenderParameters: {\n        badge: \"bottomright\",\n      },\n    },\n  })\n})\n",[1859],{"type":21,"tag":163,"props":1860,"children":1861},{"__ignoreMap":160},[1862,1892,1899,1937,1968,1997,2013,2034,2050,2079,2086,2093,2100],{"type":21,"tag":167,"props":1863,"children":1864},{"class":169,"line":170},[1865,1870,1875,1880,1884,1888],{"type":21,"tag":167,"props":1866,"children":1867},{"style":1026},[1868],{"type":26,"value":1869},"import",{"type":21,"tag":167,"props":1871,"children":1872},{"style":186},[1873],{"type":26,"value":1874}," { VueReCaptcha } ",{"type":21,"tag":167,"props":1876,"children":1877},{"style":174},[1878],{"type":26,"value":1879},"from",{"type":21,"tag":167,"props":1881,"children":1882},{"style":336},[1883],{"type":26,"value":376},{"type":21,"tag":167,"props":1885,"children":1886},{"style":342},[1887],{"type":26,"value":1813},{"type":21,"tag":167,"props":1889,"children":1890},{"style":336},[1891],{"type":26,"value":939},{"type":21,"tag":167,"props":1893,"children":1894},{"class":169,"line":192},[1895],{"type":21,"tag":167,"props":1896,"children":1897},{"emptyLinePlaceholder":876},[1898],{"type":26,"value":879},{"type":21,"tag":167,"props":1900,"children":1901},{"class":169,"line":219},[1902,1906,1910,1915,1920,1925,1929,1933],{"type":21,"tag":167,"props":1903,"children":1904},{"style":1026},[1905],{"type":26,"value":1029},{"type":21,"tag":167,"props":1907,"children":1908},{"style":174},[1909],{"type":26,"value":1034},{"type":21,"tag":167,"props":1911,"children":1912},{"style":180},[1913],{"type":26,"value":1914}," defineNuxtPlugin",{"type":21,"tag":167,"props":1916,"children":1917},{"style":186},[1918],{"type":26,"value":1919},"((",{"type":21,"tag":167,"props":1921,"children":1922},{"style":196},[1923],{"type":26,"value":1924},"nuxtApp",{"type":21,"tag":167,"props":1926,"children":1927},{"style":186},[1928],{"type":26,"value":1062},{"type":21,"tag":167,"props":1930,"children":1931},{"style":305},[1932],{"type":26,"value":1067},{"type":21,"tag":167,"props":1934,"children":1935},{"style":186},[1936],{"type":26,"value":298},{"type":21,"tag":167,"props":1938,"children":1939},{"class":169,"line":240},[1940,1945,1949,1954,1958,1963],{"type":21,"tag":167,"props":1941,"children":1942},{"style":186},[1943],{"type":26,"value":1944},"  nuxtApp",{"type":21,"tag":167,"props":1946,"children":1947},{"style":543},[1948],{"type":26,"value":546},{"type":21,"tag":167,"props":1950,"children":1951},{"style":186},[1952],{"type":26,"value":1953},"vueApp",{"type":21,"tag":167,"props":1955,"children":1956},{"style":543},[1957],{"type":26,"value":546},{"type":21,"tag":167,"props":1959,"children":1960},{"style":180},[1961],{"type":26,"value":1962},"use",{"type":21,"tag":167,"props":1964,"children":1965},{"style":186},[1966],{"type":26,"value":1967},"(VueReCaptcha, {\n",{"type":21,"tag":167,"props":1969,"children":1970},{"class":169,"line":261},[1971,1976,1980,1984,1989,1993],{"type":21,"tag":167,"props":1972,"children":1973},{"style":186},[1974],{"type":26,"value":1975},"    siteKey",{"type":21,"tag":167,"props":1977,"children":1978},{"style":202},[1979],{"type":26,"value":205},{"type":21,"tag":167,"props":1981,"children":1982},{"style":336},[1983],{"type":26,"value":376},{"type":21,"tag":167,"props":1985,"children":1986},{"style":342},[1987],{"type":26,"value":1988},"[your site key here]",{"type":21,"tag":167,"props":1990,"children":1991},{"style":336},[1992],{"type":26,"value":339},{"type":21,"tag":167,"props":1994,"children":1995},{"style":186},[1996],{"type":26,"value":216},{"type":21,"tag":167,"props":1998,"children":1999},{"class":169,"line":279},[2000,2005,2009],{"type":21,"tag":167,"props":2001,"children":2002},{"style":186},[2003],{"type":26,"value":2004},"    loaderOptions",{"type":21,"tag":167,"props":2006,"children":2007},{"style":202},[2008],{"type":26,"value":205},{"type":21,"tag":167,"props":2010,"children":2011},{"style":186},[2012],{"type":26,"value":298},{"type":21,"tag":167,"props":2014,"children":2015},{"class":169,"line":301},[2016,2021,2025,2030],{"type":21,"tag":167,"props":2017,"children":2018},{"style":186},[2019],{"type":26,"value":2020},"      autoHideBadge",{"type":21,"tag":167,"props":2022,"children":2023},{"style":202},[2024],{"type":26,"value":205},{"type":21,"tag":167,"props":2026,"children":2027},{"style":1191},[2028],{"type":26,"value":2029}," true",{"type":21,"tag":167,"props":2031,"children":2032},{"style":186},[2033],{"type":26,"value":216},{"type":21,"tag":167,"props":2035,"children":2036},{"class":169,"line":361},[2037,2042,2046],{"type":21,"tag":167,"props":2038,"children":2039},{"style":186},[2040],{"type":26,"value":2041},"      explicitRenderParameters",{"type":21,"tag":167,"props":2043,"children":2044},{"style":202},[2045],{"type":26,"value":205},{"type":21,"tag":167,"props":2047,"children":2048},{"style":186},[2049],{"type":26,"value":298},{"type":21,"tag":167,"props":2051,"children":2052},{"class":169,"line":392},[2053,2058,2062,2066,2071,2075],{"type":21,"tag":167,"props":2054,"children":2055},{"style":186},[2056],{"type":26,"value":2057},"        badge",{"type":21,"tag":167,"props":2059,"children":2060},{"style":202},[2061],{"type":26,"value":205},{"type":21,"tag":167,"props":2063,"children":2064},{"style":336},[2065],{"type":26,"value":376},{"type":21,"tag":167,"props":2067,"children":2068},{"style":342},[2069],{"type":26,"value":2070},"bottomright",{"type":21,"tag":167,"props":2072,"children":2073},{"style":336},[2074],{"type":26,"value":339},{"type":21,"tag":167,"props":2076,"children":2077},{"style":186},[2078],{"type":26,"value":216},{"type":21,"tag":167,"props":2080,"children":2081},{"class":169,"line":409},[2082],{"type":21,"tag":167,"props":2083,"children":2084},{"style":186},[2085],{"type":26,"value":634},{"type":21,"tag":167,"props":2087,"children":2088},{"class":169,"line":439},[2089],{"type":21,"tag":167,"props":2090,"children":2091},{"style":186},[2092],{"type":26,"value":521},{"type":21,"tag":167,"props":2094,"children":2095},{"class":169,"line":478},[2096],{"type":21,"tag":167,"props":2097,"children":2098},{"style":186},[2099],{"type":26,"value":869},{"type":21,"tag":167,"props":2101,"children":2102},{"class":169,"line":515},[2103],{"type":21,"tag":167,"props":2104,"children":2105},{"style":186},[2106],{"type":26,"value":1274},{"type":21,"tag":22,"props":2108,"children":2109},{},[2110],{"type":26,"value":2111},"Next we'll need to call it from our contact form to get a token we can send to the server when the form is submitted.",{"type":21,"tag":155,"props":2113,"children":2117},{"className":2114,"code":2115,"language":2116,"meta":160,"style":160},"language-html shiki shiki-themes everforest-light dracula","\u003Cscript setup lang=\"ts\">\n  import { useReCaptcha } from \"vue-recaptcha-v3\"\n\n  const recaptchaInstance = useReCaptcha()\n  const recaptcha = async () => {\n    await recaptchaInstance?.recaptchaLoaded()\n    return await recaptchaInstance?.executeRecaptcha(\"emailSender\")\n  }\n\n  const sendMessage = async (e: Event) => {\n      const token = await recaptcha()\n      const result = await fetch(\"/api/sendContactForm\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          token: token,\n          email: email.value,\n          firstName: firstName.value,\n          lastName: lastName.value,\n          message: message.value,\n        }),\n      })\n  })\n  //...\n\u003C/script>\n","html",[2118],{"type":21,"tag":163,"props":2119,"children":2120},{"__ignoreMap":160},[2121,2170,2200,2207,2232,2266,2288,2329,2336,2343,2393,2422,2465,2493,2509,2545,2553,2581,2598,2622,2647,2672,2696,2704,2712,2719,2727],{"type":21,"tag":167,"props":2122,"children":2123},{"class":169,"line":170},[2124,2130,2135,2141,2146,2150,2155,2161,2165],{"type":21,"tag":167,"props":2125,"children":2127},{"style":2126},"--shiki-default:#8DA101;--shiki-dark:#F8F8F2",[2128],{"type":26,"value":2129},"\u003C",{"type":21,"tag":167,"props":2131,"children":2132},{"style":305},[2133],{"type":26,"value":2134},"script",{"type":21,"tag":167,"props":2136,"children":2138},{"style":2137},"--shiki-default:#DFA000;--shiki-default-font-style:inherit;--shiki-dark:#50FA7B;--shiki-dark-font-style:italic",[2139],{"type":26,"value":2140}," setup",{"type":21,"tag":167,"props":2142,"children":2143},{"style":2137},[2144],{"type":26,"value":2145}," lang",{"type":21,"tag":167,"props":2147,"children":2148},{"style":759},[2149],{"type":26,"value":1097},{"type":21,"tag":167,"props":2151,"children":2153},{"style":2152},"--shiki-default:#8DA101;--shiki-dark:#E9F284",[2154],{"type":26,"value":339},{"type":21,"tag":167,"props":2156,"children":2158},{"style":2157},"--shiki-default:#8DA101;--shiki-dark:#F1FA8C",[2159],{"type":26,"value":2160},"ts",{"type":21,"tag":167,"props":2162,"children":2163},{"style":2152},[2164],{"type":26,"value":339},{"type":21,"tag":167,"props":2166,"children":2167},{"style":2126},[2168],{"type":26,"value":2169},">\n",{"type":21,"tag":167,"props":2171,"children":2172},{"class":169,"line":192},[2173,2179,2184,2188,2192,2196],{"type":21,"tag":167,"props":2174,"children":2176},{"style":2175},"--shiki-default:#35A77C;--shiki-dark:#FF79C6",[2177],{"type":26,"value":2178},"  import",{"type":21,"tag":167,"props":2180,"children":2181},{"style":186},[2182],{"type":26,"value":2183}," { useReCaptcha } ",{"type":21,"tag":167,"props":2185,"children":2186},{"style":174},[2187],{"type":26,"value":1879},{"type":21,"tag":167,"props":2189,"children":2190},{"style":336},[2191],{"type":26,"value":376},{"type":21,"tag":167,"props":2193,"children":2194},{"style":342},[2195],{"type":26,"value":1813},{"type":21,"tag":167,"props":2197,"children":2198},{"style":336},[2199],{"type":26,"value":939},{"type":21,"tag":167,"props":2201,"children":2202},{"class":169,"line":219},[2203],{"type":21,"tag":167,"props":2204,"children":2205},{"emptyLinePlaceholder":876},[2206],{"type":26,"value":879},{"type":21,"tag":167,"props":2208,"children":2209},{"class":169,"line":240},[2210,2214,2219,2223,2228],{"type":21,"tag":167,"props":2211,"children":2212},{"style":305},[2213],{"type":26,"value":308},{"type":21,"tag":167,"props":2215,"children":2216},{"style":186},[2217],{"type":26,"value":2218}," recaptchaInstance ",{"type":21,"tag":167,"props":2220,"children":2221},{"style":305},[2222],{"type":26,"value":1097},{"type":21,"tag":167,"props":2224,"children":2225},{"style":180},[2226],{"type":26,"value":2227}," useReCaptcha",{"type":21,"tag":167,"props":2229,"children":2230},{"style":186},[2231],{"type":26,"value":1713},{"type":21,"tag":167,"props":2233,"children":2234},{"class":169,"line":261},[2235,2239,2244,2248,2253,2258,2262],{"type":21,"tag":167,"props":2236,"children":2237},{"style":305},[2238],{"type":26,"value":308},{"type":21,"tag":167,"props":2240,"children":2241},{"style":180},[2242],{"type":26,"value":2243}," recaptcha",{"type":21,"tag":167,"props":2245,"children":2246},{"style":305},[2247],{"type":26,"value":318},{"type":21,"tag":167,"props":2249,"children":2250},{"style":305},[2251],{"type":26,"value":2252}," async",{"type":21,"tag":167,"props":2254,"children":2255},{"style":186},[2256],{"type":26,"value":2257}," () ",{"type":21,"tag":167,"props":2259,"children":2260},{"style":305},[2261],{"type":26,"value":1067},{"type":21,"tag":167,"props":2263,"children":2264},{"style":186},[2265],{"type":26,"value":298},{"type":21,"tag":167,"props":2267,"children":2268},{"class":169,"line":279},[2269,2274,2279,2284],{"type":21,"tag":167,"props":2270,"children":2271},{"style":174},[2272],{"type":26,"value":2273},"    await",{"type":21,"tag":167,"props":2275,"children":2276},{"style":186},[2277],{"type":26,"value":2278}," recaptchaInstance?.",{"type":21,"tag":167,"props":2280,"children":2281},{"style":180},[2282],{"type":26,"value":2283},"recaptchaLoaded",{"type":21,"tag":167,"props":2285,"children":2286},{"style":186},[2287],{"type":26,"value":1713},{"type":21,"tag":167,"props":2289,"children":2290},{"class":169,"line":301},[2291,2295,2299,2303,2308,2312,2316,2321,2325],{"type":21,"tag":167,"props":2292,"children":2293},{"style":174},[2294],{"type":26,"value":925},{"type":21,"tag":167,"props":2296,"children":2297},{"style":174},[2298],{"type":26,"value":323},{"type":21,"tag":167,"props":2300,"children":2301},{"style":186},[2302],{"type":26,"value":2278},{"type":21,"tag":167,"props":2304,"children":2305},{"style":180},[2306],{"type":26,"value":2307},"executeRecaptcha",{"type":21,"tag":167,"props":2309,"children":2310},{"style":186},[2311],{"type":26,"value":333},{"type":21,"tag":167,"props":2313,"children":2314},{"style":336},[2315],{"type":26,"value":339},{"type":21,"tag":167,"props":2317,"children":2318},{"style":342},[2319],{"type":26,"value":2320},"emailSender",{"type":21,"tag":167,"props":2322,"children":2323},{"style":336},[2324],{"type":26,"value":339},{"type":21,"tag":167,"props":2326,"children":2327},{"style":186},[2328],{"type":26,"value":1345},{"type":21,"tag":167,"props":2330,"children":2331},{"class":169,"line":361},[2332],{"type":21,"tag":167,"props":2333,"children":2334},{"style":186},[2335],{"type":26,"value":948},{"type":21,"tag":167,"props":2337,"children":2338},{"class":169,"line":392},[2339],{"type":21,"tag":167,"props":2340,"children":2341},{"emptyLinePlaceholder":876},[2342],{"type":26,"value":879},{"type":21,"tag":167,"props":2344,"children":2345},{"class":169,"line":409},[2346,2350,2355,2359,2363,2367,2372,2376,2381,2385,2389],{"type":21,"tag":167,"props":2347,"children":2348},{"style":305},[2349],{"type":26,"value":308},{"type":21,"tag":167,"props":2351,"children":2352},{"style":180},[2353],{"type":26,"value":2354}," sendMessage",{"type":21,"tag":167,"props":2356,"children":2357},{"style":305},[2358],{"type":26,"value":318},{"type":21,"tag":167,"props":2360,"children":2361},{"style":305},[2362],{"type":26,"value":2252},{"type":21,"tag":167,"props":2364,"children":2365},{"style":186},[2366],{"type":26,"value":893},{"type":21,"tag":167,"props":2368,"children":2369},{"style":196},[2370],{"type":26,"value":2371},"e",{"type":21,"tag":167,"props":2373,"children":2374},{"style":305},[2375],{"type":26,"value":205},{"type":21,"tag":167,"props":2377,"children":2378},{"style":208},[2379],{"type":26,"value":2380}," Event",{"type":21,"tag":167,"props":2382,"children":2383},{"style":186},[2384],{"type":26,"value":1062},{"type":21,"tag":167,"props":2386,"children":2387},{"style":305},[2388],{"type":26,"value":1067},{"type":21,"tag":167,"props":2390,"children":2391},{"style":186},[2392],{"type":26,"value":298},{"type":21,"tag":167,"props":2394,"children":2395},{"class":169,"line":439},[2396,2401,2406,2410,2414,2418],{"type":21,"tag":167,"props":2397,"children":2398},{"style":305},[2399],{"type":26,"value":2400},"      const",{"type":21,"tag":167,"props":2402,"children":2403},{"style":186},[2404],{"type":26,"value":2405}," token ",{"type":21,"tag":167,"props":2407,"children":2408},{"style":305},[2409],{"type":26,"value":1097},{"type":21,"tag":167,"props":2411,"children":2412},{"style":174},[2413],{"type":26,"value":323},{"type":21,"tag":167,"props":2415,"children":2416},{"style":180},[2417],{"type":26,"value":2243},{"type":21,"tag":167,"props":2419,"children":2420},{"style":186},[2421],{"type":26,"value":1713},{"type":21,"tag":167,"props":2423,"children":2424},{"class":169,"line":478},[2425,2429,2433,2437,2441,2445,2449,2453,2457,2461],{"type":21,"tag":167,"props":2426,"children":2427},{"style":305},[2428],{"type":26,"value":2400},{"type":21,"tag":167,"props":2430,"children":2431},{"style":186},[2432],{"type":26,"value":1428},{"type":21,"tag":167,"props":2434,"children":2435},{"style":305},[2436],{"type":26,"value":1097},{"type":21,"tag":167,"props":2438,"children":2439},{"style":174},[2440],{"type":26,"value":323},{"type":21,"tag":167,"props":2442,"children":2443},{"style":180},[2444],{"type":26,"value":328},{"type":21,"tag":167,"props":2446,"children":2447},{"style":186},[2448],{"type":26,"value":333},{"type":21,"tag":167,"props":2450,"children":2451},{"style":336},[2452],{"type":26,"value":339},{"type":21,"tag":167,"props":2454,"children":2455},{"style":342},[2456],{"type":26,"value":1453},{"type":21,"tag":167,"props":2458,"children":2459},{"style":336},[2460],{"type":26,"value":339},{"type":21,"tag":167,"props":2462,"children":2463},{"style":186},[2464],{"type":26,"value":1462},{"type":21,"tag":167,"props":2466,"children":2467},{"class":169,"line":515},[2468,2473,2477,2481,2485,2489],{"type":21,"tag":167,"props":2469,"children":2470},{"style":186},[2471],{"type":26,"value":2472},"        method",{"type":21,"tag":167,"props":2474,"children":2475},{"style":202},[2476],{"type":26,"value":205},{"type":21,"tag":167,"props":2478,"children":2479},{"style":336},[2480],{"type":26,"value":376},{"type":21,"tag":167,"props":2482,"children":2483},{"style":342},[2484],{"type":26,"value":381},{"type":21,"tag":167,"props":2486,"children":2487},{"style":336},[2488],{"type":26,"value":339},{"type":21,"tag":167,"props":2490,"children":2491},{"style":186},[2492],{"type":26,"value":216},{"type":21,"tag":167,"props":2494,"children":2495},{"class":169,"line":524},[2496,2501,2505],{"type":21,"tag":167,"props":2497,"children":2498},{"style":186},[2499],{"type":26,"value":2500},"        headers",{"type":21,"tag":167,"props":2502,"children":2503},{"style":202},[2504],{"type":26,"value":205},{"type":21,"tag":167,"props":2506,"children":2507},{"style":186},[2508],{"type":26,"value":298},{"type":21,"tag":167,"props":2510,"children":2511},{"class":169,"line":559},[2512,2517,2521,2525,2529,2533,2537,2541],{"type":21,"tag":167,"props":2513,"children":2514},{"style":336},[2515],{"type":26,"value":2516},"          \"",{"type":21,"tag":167,"props":2518,"children":2519},{"style":342},[2520],{"type":26,"value":1519},{"type":21,"tag":167,"props":2522,"children":2523},{"style":336},[2524],{"type":26,"value":339},{"type":21,"tag":167,"props":2526,"children":2527},{"style":202},[2528],{"type":26,"value":205},{"type":21,"tag":167,"props":2530,"children":2531},{"style":336},[2532],{"type":26,"value":376},{"type":21,"tag":167,"props":2534,"children":2535},{"style":342},[2536],{"type":26,"value":428},{"type":21,"tag":167,"props":2538,"children":2539},{"style":336},[2540],{"type":26,"value":339},{"type":21,"tag":167,"props":2542,"children":2543},{"style":186},[2544],{"type":26,"value":216},{"type":21,"tag":167,"props":2546,"children":2547},{"class":169,"line":576},[2548],{"type":21,"tag":167,"props":2549,"children":2550},{"style":186},[2551],{"type":26,"value":2552},"        },\n",{"type":21,"tag":167,"props":2554,"children":2555},{"class":169,"line":598},[2556,2561,2565,2569,2573,2577],{"type":21,"tag":167,"props":2557,"children":2558},{"style":186},[2559],{"type":26,"value":2560},"        body",{"type":21,"tag":167,"props":2562,"children":2563},{"style":202},[2564],{"type":26,"value":205},{"type":21,"tag":167,"props":2566,"children":2567},{"style":537},[2568],{"type":26,"value":540},{"type":21,"tag":167,"props":2570,"children":2571},{"style":543},[2572],{"type":26,"value":546},{"type":21,"tag":167,"props":2574,"children":2575},{"style":180},[2576],{"type":26,"value":551},{"type":21,"tag":167,"props":2578,"children":2579},{"style":186},[2580],{"type":26,"value":556},{"type":21,"tag":167,"props":2582,"children":2583},{"class":169,"line":628},[2584,2589,2593],{"type":21,"tag":167,"props":2585,"children":2586},{"style":186},[2587],{"type":26,"value":2588},"          token",{"type":21,"tag":167,"props":2590,"children":2591},{"style":202},[2592],{"type":26,"value":205},{"type":21,"tag":167,"props":2594,"children":2595},{"style":186},[2596],{"type":26,"value":2597}," token,\n",{"type":21,"tag":167,"props":2599,"children":2600},{"class":169,"line":637},[2601,2606,2610,2614,2618],{"type":21,"tag":167,"props":2602,"children":2603},{"style":186},[2604],{"type":26,"value":2605},"          email",{"type":21,"tag":167,"props":2607,"children":2608},{"style":202},[2609],{"type":26,"value":205},{"type":21,"tag":167,"props":2611,"children":2612},{"style":186},[2613],{"type":26,"value":657},{"type":21,"tag":167,"props":2615,"children":2616},{"style":543},[2617],{"type":26,"value":546},{"type":21,"tag":167,"props":2619,"children":2620},{"style":186},[2621],{"type":26,"value":1604},{"type":21,"tag":167,"props":2623,"children":2624},{"class":169,"line":708},[2625,2630,2634,2639,2643],{"type":21,"tag":167,"props":2626,"children":2627},{"style":186},[2628],{"type":26,"value":2629},"          firstName",{"type":21,"tag":167,"props":2631,"children":2632},{"style":202},[2633],{"type":26,"value":205},{"type":21,"tag":167,"props":2635,"children":2636},{"style":186},[2637],{"type":26,"value":2638}," firstName",{"type":21,"tag":167,"props":2640,"children":2641},{"style":543},[2642],{"type":26,"value":546},{"type":21,"tag":167,"props":2644,"children":2645},{"style":186},[2646],{"type":26,"value":1604},{"type":21,"tag":167,"props":2648,"children":2649},{"class":169,"line":725},[2650,2655,2659,2664,2668],{"type":21,"tag":167,"props":2651,"children":2652},{"style":186},[2653],{"type":26,"value":2654},"          lastName",{"type":21,"tag":167,"props":2656,"children":2657},{"style":202},[2658],{"type":26,"value":205},{"type":21,"tag":167,"props":2660,"children":2661},{"style":186},[2662],{"type":26,"value":2663}," lastName",{"type":21,"tag":167,"props":2665,"children":2666},{"style":543},[2667],{"type":26,"value":546},{"type":21,"tag":167,"props":2669,"children":2670},{"style":186},[2671],{"type":26,"value":1604},{"type":21,"tag":167,"props":2673,"children":2674},{"class":169,"line":742},[2675,2680,2684,2688,2692],{"type":21,"tag":167,"props":2676,"children":2677},{"style":186},[2678],{"type":26,"value":2679},"          message",{"type":21,"tag":167,"props":2681,"children":2682},{"style":202},[2683],{"type":26,"value":205},{"type":21,"tag":167,"props":2685,"children":2686},{"style":186},[2687],{"type":26,"value":1645},{"type":21,"tag":167,"props":2689,"children":2690},{"style":543},[2691],{"type":26,"value":546},{"type":21,"tag":167,"props":2693,"children":2694},{"style":186},[2695],{"type":26,"value":1604},{"type":21,"tag":167,"props":2697,"children":2698},{"class":169,"line":798},[2699],{"type":21,"tag":167,"props":2700,"children":2701},{"style":186},[2702],{"type":26,"value":2703},"        }),\n",{"type":21,"tag":167,"props":2705,"children":2706},{"class":169,"line":806},[2707],{"type":21,"tag":167,"props":2708,"children":2709},{"style":186},[2710],{"type":26,"value":2711},"      })\n",{"type":21,"tag":167,"props":2713,"children":2714},{"class":169,"line":836},[2715],{"type":21,"tag":167,"props":2716,"children":2717},{"style":186},[2718],{"type":26,"value":869},{"type":21,"tag":167,"props":2720,"children":2721},{"class":169,"line":854},[2722],{"type":21,"tag":167,"props":2723,"children":2724},{"style":1077},[2725],{"type":26,"value":2726},"  //...\n",{"type":21,"tag":167,"props":2728,"children":2729},{"class":169,"line":863},[2730,2735,2739],{"type":21,"tag":167,"props":2731,"children":2732},{"style":2126},[2733],{"type":26,"value":2734},"\u003C/",{"type":21,"tag":167,"props":2736,"children":2737},{"style":305},[2738],{"type":26,"value":2134},{"type":21,"tag":167,"props":2740,"children":2741},{"style":2126},[2742],{"type":26,"value":2169},{"type":21,"tag":83,"props":2744,"children":2746},{"id":2745},"server-side-validation",[2747],{"type":26,"value":2748},"Server-side validation",{"type":21,"tag":22,"props":2750,"children":2751},{},[2752],{"type":26,"value":2753},"Now that we have the client-side taken care of we turn to the server-side which will send the token to Google and get the score. Here's a little function to do just that.",{"type":21,"tag":155,"props":2755,"children":2757},{"className":157,"code":2756,"language":159,"meta":160,"style":160},"function validateCaptcha(token: string): string {\n  const response = await fetch(\n    \"https://www.google.com/recaptcha/api/siteverify\",\n    {\n      method: \"POST\",\n      headers: {\n        \"content-type\": \"application/x-www-form-urlencoded\",\n      },\n      body: `secret=${process.env.RECAPTCHA_SITE_KEY}&response=${token}`,\n    }\n  )\n\n  const verifyBody = await response.json()\n  if (!verifyBody.ok) return \"Unable to validate captcha at this time.\"\n  if (verifyBody.success !== true || verifyBody.score \u003C 0.5)\n    return \"Invalid captcha response.\"\n\n  return \"ok\"\n}\n",[2758],{"type":21,"tag":163,"props":2759,"children":2760},{"__ignoreMap":160},[2761,2806,2833,2853,2861,2889,2905,2942,2949,3022,3030,3038,3045,3082,3128,3199,3219,3226,3246],{"type":21,"tag":167,"props":2762,"children":2763},{"class":169,"line":170},[2764,2768,2773,2777,2782,2786,2790,2794,2798,2802],{"type":21,"tag":167,"props":2765,"children":2766},{"style":174},[2767],{"type":26,"value":177},{"type":21,"tag":167,"props":2769,"children":2770},{"style":180},[2771],{"type":26,"value":2772}," validateCaptcha",{"type":21,"tag":167,"props":2774,"children":2775},{"style":186},[2776],{"type":26,"value":333},{"type":21,"tag":167,"props":2778,"children":2779},{"style":196},[2780],{"type":26,"value":2781},"token",{"type":21,"tag":167,"props":2783,"children":2784},{"style":202},[2785],{"type":26,"value":205},{"type":21,"tag":167,"props":2787,"children":2788},{"style":208},[2789],{"type":26,"value":211},{"type":21,"tag":167,"props":2791,"children":2792},{"style":186},[2793],{"type":26,"value":285},{"type":21,"tag":167,"props":2795,"children":2796},{"style":202},[2797],{"type":26,"value":205},{"type":21,"tag":167,"props":2799,"children":2800},{"style":208},[2801],{"type":26,"value":211},{"type":21,"tag":167,"props":2803,"children":2804},{"style":186},[2805],{"type":26,"value":298},{"type":21,"tag":167,"props":2807,"children":2808},{"class":169,"line":192},[2809,2813,2817,2821,2825,2829],{"type":21,"tag":167,"props":2810,"children":2811},{"style":305},[2812],{"type":26,"value":308},{"type":21,"tag":167,"props":2814,"children":2815},{"style":186},[2816],{"type":26,"value":313},{"type":21,"tag":167,"props":2818,"children":2819},{"style":305},[2820],{"type":26,"value":318},{"type":21,"tag":167,"props":2822,"children":2823},{"style":174},[2824],{"type":26,"value":323},{"type":21,"tag":167,"props":2826,"children":2827},{"style":180},[2828],{"type":26,"value":328},{"type":21,"tag":167,"props":2830,"children":2831},{"style":186},[2832],{"type":26,"value":189},{"type":21,"tag":167,"props":2834,"children":2835},{"class":169,"line":219},[2836,2840,2845,2849],{"type":21,"tag":167,"props":2837,"children":2838},{"style":336},[2839],{"type":26,"value":1514},{"type":21,"tag":167,"props":2841,"children":2842},{"style":342},[2843],{"type":26,"value":2844},"https://www.google.com/recaptcha/api/siteverify",{"type":21,"tag":167,"props":2846,"children":2847},{"style":336},[2848],{"type":26,"value":339},{"type":21,"tag":167,"props":2850,"children":2851},{"style":186},[2852],{"type":26,"value":216},{"type":21,"tag":167,"props":2854,"children":2855},{"class":169,"line":240},[2856],{"type":21,"tag":167,"props":2857,"children":2858},{"style":186},[2859],{"type":26,"value":2860},"    {\n",{"type":21,"tag":167,"props":2862,"children":2863},{"class":169,"line":261},[2864,2869,2873,2877,2881,2885],{"type":21,"tag":167,"props":2865,"children":2866},{"style":186},[2867],{"type":26,"value":2868},"      method",{"type":21,"tag":167,"props":2870,"children":2871},{"style":202},[2872],{"type":26,"value":205},{"type":21,"tag":167,"props":2874,"children":2875},{"style":336},[2876],{"type":26,"value":376},{"type":21,"tag":167,"props":2878,"children":2879},{"style":342},[2880],{"type":26,"value":381},{"type":21,"tag":167,"props":2882,"children":2883},{"style":336},[2884],{"type":26,"value":339},{"type":21,"tag":167,"props":2886,"children":2887},{"style":186},[2888],{"type":26,"value":216},{"type":21,"tag":167,"props":2890,"children":2891},{"class":169,"line":279},[2892,2897,2901],{"type":21,"tag":167,"props":2893,"children":2894},{"style":186},[2895],{"type":26,"value":2896},"      headers",{"type":21,"tag":167,"props":2898,"children":2899},{"style":202},[2900],{"type":26,"value":205},{"type":21,"tag":167,"props":2902,"children":2903},{"style":186},[2904],{"type":26,"value":298},{"type":21,"tag":167,"props":2906,"children":2907},{"class":169,"line":301},[2908,2913,2917,2921,2925,2929,2934,2938],{"type":21,"tag":167,"props":2909,"children":2910},{"style":336},[2911],{"type":26,"value":2912},"        \"",{"type":21,"tag":167,"props":2914,"children":2915},{"style":342},[2916],{"type":26,"value":488},{"type":21,"tag":167,"props":2918,"children":2919},{"style":336},[2920],{"type":26,"value":339},{"type":21,"tag":167,"props":2922,"children":2923},{"style":202},[2924],{"type":26,"value":205},{"type":21,"tag":167,"props":2926,"children":2927},{"style":336},[2928],{"type":26,"value":376},{"type":21,"tag":167,"props":2930,"children":2931},{"style":342},[2932],{"type":26,"value":2933},"application/x-www-form-urlencoded",{"type":21,"tag":167,"props":2935,"children":2936},{"style":336},[2937],{"type":26,"value":339},{"type":21,"tag":167,"props":2939,"children":2940},{"style":186},[2941],{"type":26,"value":216},{"type":21,"tag":167,"props":2943,"children":2944},{"class":169,"line":361},[2945],{"type":21,"tag":167,"props":2946,"children":2947},{"style":186},[2948],{"type":26,"value":634},{"type":21,"tag":167,"props":2950,"children":2951},{"class":169,"line":392},[2952,2957,2961,2966,2970,2975,2979,2984,2988,2993,2997,3002,3006,3010,3014,3018],{"type":21,"tag":167,"props":2953,"children":2954},{"style":186},[2955],{"type":26,"value":2956},"      body",{"type":21,"tag":167,"props":2958,"children":2959},{"style":202},[2960],{"type":26,"value":205},{"type":21,"tag":167,"props":2962,"children":2963},{"style":342},[2964],{"type":26,"value":2965}," `secret=",{"type":21,"tag":167,"props":2967,"children":2968},{"style":759},[2969],{"type":26,"value":762},{"type":21,"tag":167,"props":2971,"children":2972},{"style":186},[2973],{"type":26,"value":2974},"process",{"type":21,"tag":167,"props":2976,"children":2977},{"style":543},[2978],{"type":26,"value":546},{"type":21,"tag":167,"props":2980,"children":2981},{"style":186},[2982],{"type":26,"value":2983},"env",{"type":21,"tag":167,"props":2985,"children":2986},{"style":543},[2987],{"type":26,"value":546},{"type":21,"tag":167,"props":2989,"children":2990},{"style":537},[2991],{"type":26,"value":2992},"RECAPTCHA_SITE_KEY",{"type":21,"tag":167,"props":2994,"children":2995},{"style":759},[2996],{"type":26,"value":772},{"type":21,"tag":167,"props":2998,"children":2999},{"style":342},[3000],{"type":26,"value":3001},"&response=",{"type":21,"tag":167,"props":3003,"children":3004},{"style":759},[3005],{"type":26,"value":762},{"type":21,"tag":167,"props":3007,"children":3008},{"style":186},[3009],{"type":26,"value":2781},{"type":21,"tag":167,"props":3011,"children":3012},{"style":759},[3013],{"type":26,"value":772},{"type":21,"tag":167,"props":3015,"children":3016},{"style":342},[3017],{"type":26,"value":791},{"type":21,"tag":167,"props":3019,"children":3020},{"style":186},[3021],{"type":26,"value":216},{"type":21,"tag":167,"props":3023,"children":3024},{"class":169,"line":409},[3025],{"type":21,"tag":167,"props":3026,"children":3027},{"style":186},[3028],{"type":26,"value":3029},"    }\n",{"type":21,"tag":167,"props":3031,"children":3032},{"class":169,"line":439},[3033],{"type":21,"tag":167,"props":3034,"children":3035},{"style":186},[3036],{"type":26,"value":3037},"  )\n",{"type":21,"tag":167,"props":3039,"children":3040},{"class":169,"line":478},[3041],{"type":21,"tag":167,"props":3042,"children":3043},{"emptyLinePlaceholder":876},[3044],{"type":26,"value":879},{"type":21,"tag":167,"props":3046,"children":3047},{"class":169,"line":515},[3048,3052,3057,3061,3065,3069,3073,3078],{"type":21,"tag":167,"props":3049,"children":3050},{"style":305},[3051],{"type":26,"value":308},{"type":21,"tag":167,"props":3053,"children":3054},{"style":186},[3055],{"type":26,"value":3056}," verifyBody",{"type":21,"tag":167,"props":3058,"children":3059},{"style":305},[3060],{"type":26,"value":318},{"type":21,"tag":167,"props":3062,"children":3063},{"style":174},[3064],{"type":26,"value":323},{"type":21,"tag":167,"props":3066,"children":3067},{"style":186},[3068],{"type":26,"value":313},{"type":21,"tag":167,"props":3070,"children":3071},{"style":543},[3072],{"type":26,"value":546},{"type":21,"tag":167,"props":3074,"children":3075},{"style":180},[3076],{"type":26,"value":3077},"json",{"type":21,"tag":167,"props":3079,"children":3080},{"style":186},[3081],{"type":26,"value":1713},{"type":21,"tag":167,"props":3083,"children":3084},{"class":169,"line":524},[3085,3089,3093,3097,3102,3106,3110,3115,3119,3124],{"type":21,"tag":167,"props":3086,"children":3087},{"style":174},[3088],{"type":26,"value":888},{"type":21,"tag":167,"props":3090,"children":3091},{"style":186},[3092],{"type":26,"value":893},{"type":21,"tag":167,"props":3094,"children":3095},{"style":305},[3096],{"type":26,"value":898},{"type":21,"tag":167,"props":3098,"children":3099},{"style":186},[3100],{"type":26,"value":3101},"verifyBody",{"type":21,"tag":167,"props":3103,"children":3104},{"style":543},[3105],{"type":26,"value":546},{"type":21,"tag":167,"props":3107,"children":3108},{"style":186},[3109],{"type":26,"value":912},{"type":21,"tag":167,"props":3111,"children":3112},{"style":174},[3113],{"type":26,"value":3114}," return",{"type":21,"tag":167,"props":3116,"children":3117},{"style":336},[3118],{"type":26,"value":376},{"type":21,"tag":167,"props":3120,"children":3121},{"style":342},[3122],{"type":26,"value":3123},"Unable to validate captcha at this time.",{"type":21,"tag":167,"props":3125,"children":3126},{"style":336},[3127],{"type":26,"value":939},{"type":21,"tag":167,"props":3129,"children":3130},{"class":169,"line":559},[3131,3135,3140,3144,3149,3154,3158,3163,3167,3171,3176,3181,3186,3190,3195],{"type":21,"tag":167,"props":3132,"children":3133},{"style":174},[3134],{"type":26,"value":888},{"type":21,"tag":167,"props":3136,"children":3137},{"style":186},[3138],{"type":26,"value":3139}," (verifyBody",{"type":21,"tag":167,"props":3141,"children":3142},{"style":543},[3143],{"type":26,"value":546},{"type":21,"tag":167,"props":3145,"children":3146},{"style":186},[3147],{"type":26,"value":3148},"success",{"type":21,"tag":167,"props":3150,"children":3151},{"style":305},[3152],{"type":26,"value":3153}," !==",{"type":21,"tag":167,"props":3155,"children":3156},{"style":1191},[3157],{"type":26,"value":2029},{"type":21,"tag":167,"props":3159,"children":3160},{"style":305},[3161],{"type":26,"value":3162}," ||",{"type":21,"tag":167,"props":3164,"children":3165},{"style":186},[3166],{"type":26,"value":3056},{"type":21,"tag":167,"props":3168,"children":3169},{"style":543},[3170],{"type":26,"value":546},{"type":21,"tag":167,"props":3172,"children":3173},{"style":186},[3174],{"type":26,"value":3175},"score",{"type":21,"tag":167,"props":3177,"children":3178},{"style":305},[3179],{"type":26,"value":3180}," \u003C",{"type":21,"tag":167,"props":3182,"children":3183},{"style":1191},[3184],{"type":26,"value":3185}," 0",{"type":21,"tag":167,"props":3187,"children":3188},{"style":537},[3189],{"type":26,"value":546},{"type":21,"tag":167,"props":3191,"children":3192},{"style":1191},[3193],{"type":26,"value":3194},"5",{"type":21,"tag":167,"props":3196,"children":3197},{"style":186},[3198],{"type":26,"value":1345},{"type":21,"tag":167,"props":3200,"children":3201},{"class":169,"line":576},[3202,3206,3210,3215],{"type":21,"tag":167,"props":3203,"children":3204},{"style":174},[3205],{"type":26,"value":925},{"type":21,"tag":167,"props":3207,"children":3208},{"style":336},[3209],{"type":26,"value":376},{"type":21,"tag":167,"props":3211,"children":3212},{"style":342},[3213],{"type":26,"value":3214},"Invalid captcha response.",{"type":21,"tag":167,"props":3216,"children":3217},{"style":336},[3218],{"type":26,"value":939},{"type":21,"tag":167,"props":3220,"children":3221},{"class":169,"line":598},[3222],{"type":21,"tag":167,"props":3223,"children":3224},{"emptyLinePlaceholder":876},[3225],{"type":26,"value":879},{"type":21,"tag":167,"props":3227,"children":3228},{"class":169,"line":628},[3229,3233,3237,3242],{"type":21,"tag":167,"props":3230,"children":3231},{"style":174},[3232],{"type":26,"value":1257},{"type":21,"tag":167,"props":3234,"children":3235},{"style":336},[3236],{"type":26,"value":376},{"type":21,"tag":167,"props":3238,"children":3239},{"style":342},[3240],{"type":26,"value":3241},"ok",{"type":21,"tag":167,"props":3243,"children":3244},{"style":336},[3245],{"type":26,"value":939},{"type":21,"tag":167,"props":3247,"children":3248},{"class":169,"line":637},[3249],{"type":21,"tag":167,"props":3250,"children":3251},{"style":186},[3252],{"type":26,"value":957},{"type":21,"tag":22,"props":3254,"children":3255},{},[3256],{"type":26,"value":3257},"And now we can revise our handler to call our new validation function and decide whether to send the email or return an error.",{"type":21,"tag":155,"props":3259,"children":3261},{"className":157,"code":3260,"language":159,"meta":160,"style":160},"export default defineEventHandler(async (event) => {\n  // Validate parameters\n  const { firstName, lastName, email, message, token } = await readBody(event)\n  if (!firstName || !lastName || !email || !message) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Missing required fields\",\n    })\n  }\n\n  const captchaResult = await validateCaptcha(token)\n  if (captchaResult !== \"ok\") return captchaResult\n\n  return sendEmail(email, firstName, lastName, message)\n})\n",[3262],{"type":21,"tag":163,"props":3263,"children":3264},{"__ignoreMap":160},[3265,3308,3315,3343,3400,3415,3434,3461,3468,3475,3482,3511,3554,3561,3577],{"type":21,"tag":167,"props":3266,"children":3267},{"class":169,"line":170},[3268,3272,3276,3280,3284,3288,3292,3296,3300,3304],{"type":21,"tag":167,"props":3269,"children":3270},{"style":1026},[3271],{"type":26,"value":1029},{"type":21,"tag":167,"props":3273,"children":3274},{"style":174},[3275],{"type":26,"value":1034},{"type":21,"tag":167,"props":3277,"children":3278},{"style":180},[3279],{"type":26,"value":1039},{"type":21,"tag":167,"props":3281,"children":3282},{"style":186},[3283],{"type":26,"value":333},{"type":21,"tag":167,"props":3285,"children":3286},{"style":305},[3287],{"type":26,"value":1048},{"type":21,"tag":167,"props":3289,"children":3290},{"style":186},[3291],{"type":26,"value":893},{"type":21,"tag":167,"props":3293,"children":3294},{"style":196},[3295],{"type":26,"value":1057},{"type":21,"tag":167,"props":3297,"children":3298},{"style":186},[3299],{"type":26,"value":1062},{"type":21,"tag":167,"props":3301,"children":3302},{"style":305},[3303],{"type":26,"value":1067},{"type":21,"tag":167,"props":3305,"children":3306},{"style":186},[3307],{"type":26,"value":298},{"type":21,"tag":167,"props":3309,"children":3310},{"class":169,"line":192},[3311],{"type":21,"tag":167,"props":3312,"children":3313},{"style":1077},[3314],{"type":26,"value":1080},{"type":21,"tag":167,"props":3316,"children":3317},{"class":169,"line":219},[3318,3322,3327,3331,3335,3339],{"type":21,"tag":167,"props":3319,"children":3320},{"style":305},[3321],{"type":26,"value":308},{"type":21,"tag":167,"props":3323,"children":3324},{"style":186},[3325],{"type":26,"value":3326}," { firstName, lastName, email, message, token } ",{"type":21,"tag":167,"props":3328,"children":3329},{"style":305},[3330],{"type":26,"value":1097},{"type":21,"tag":167,"props":3332,"children":3333},{"style":174},[3334],{"type":26,"value":323},{"type":21,"tag":167,"props":3336,"children":3337},{"style":180},[3338],{"type":26,"value":1106},{"type":21,"tag":167,"props":3340,"children":3341},{"style":186},[3342],{"type":26,"value":1111},{"type":21,"tag":167,"props":3344,"children":3345},{"class":169,"line":240},[3346,3350,3354,3358,3363,3367,3371,3376,3380,3384,3388,3392,3396],{"type":21,"tag":167,"props":3347,"children":3348},{"style":174},[3349],{"type":26,"value":888},{"type":21,"tag":167,"props":3351,"children":3352},{"style":186},[3353],{"type":26,"value":893},{"type":21,"tag":167,"props":3355,"children":3356},{"style":305},[3357],{"type":26,"value":898},{"type":21,"tag":167,"props":3359,"children":3360},{"style":186},[3361],{"type":26,"value":3362},"firstName ",{"type":21,"tag":167,"props":3364,"children":3365},{"style":305},[3366],{"type":26,"value":1136},{"type":21,"tag":167,"props":3368,"children":3369},{"style":305},[3370],{"type":26,"value":1141},{"type":21,"tag":167,"props":3372,"children":3373},{"style":186},[3374],{"type":26,"value":3375},"lastName ",{"type":21,"tag":167,"props":3377,"children":3378},{"style":305},[3379],{"type":26,"value":1136},{"type":21,"tag":167,"props":3381,"children":3382},{"style":305},[3383],{"type":26,"value":1141},{"type":21,"tag":167,"props":3385,"children":3386},{"style":186},[3387],{"type":26,"value":1146},{"type":21,"tag":167,"props":3389,"children":3390},{"style":305},[3391],{"type":26,"value":1136},{"type":21,"tag":167,"props":3393,"children":3394},{"style":305},[3395],{"type":26,"value":1141},{"type":21,"tag":167,"props":3397,"children":3398},{"style":186},[3399],{"type":26,"value":1159},{"type":21,"tag":167,"props":3401,"children":3402},{"class":169,"line":261},[3403,3407,3411],{"type":21,"tag":167,"props":3404,"children":3405},{"style":174},[3406],{"type":26,"value":1167},{"type":21,"tag":167,"props":3408,"children":3409},{"style":180},[3410],{"type":26,"value":1172},{"type":21,"tag":167,"props":3412,"children":3413},{"style":186},[3414],{"type":26,"value":556},{"type":21,"tag":167,"props":3416,"children":3417},{"class":169,"line":279},[3418,3422,3426,3430],{"type":21,"tag":167,"props":3419,"children":3420},{"style":186},[3421],{"type":26,"value":1184},{"type":21,"tag":167,"props":3423,"children":3424},{"style":202},[3425],{"type":26,"value":205},{"type":21,"tag":167,"props":3427,"children":3428},{"style":1191},[3429],{"type":26,"value":1194},{"type":21,"tag":167,"props":3431,"children":3432},{"style":186},[3433],{"type":26,"value":216},{"type":21,"tag":167,"props":3435,"children":3436},{"class":169,"line":301},[3437,3441,3445,3449,3453,3457],{"type":21,"tag":167,"props":3438,"children":3439},{"style":186},[3440],{"type":26,"value":1206},{"type":21,"tag":167,"props":3442,"children":3443},{"style":202},[3444],{"type":26,"value":205},{"type":21,"tag":167,"props":3446,"children":3447},{"style":336},[3448],{"type":26,"value":376},{"type":21,"tag":167,"props":3450,"children":3451},{"style":342},[3452],{"type":26,"value":1219},{"type":21,"tag":167,"props":3454,"children":3455},{"style":336},[3456],{"type":26,"value":339},{"type":21,"tag":167,"props":3458,"children":3459},{"style":186},[3460],{"type":26,"value":216},{"type":21,"tag":167,"props":3462,"children":3463},{"class":169,"line":361},[3464],{"type":21,"tag":167,"props":3465,"children":3466},{"style":186},[3467],{"type":26,"value":1235},{"type":21,"tag":167,"props":3469,"children":3470},{"class":169,"line":392},[3471],{"type":21,"tag":167,"props":3472,"children":3473},{"style":186},[3474],{"type":26,"value":948},{"type":21,"tag":167,"props":3476,"children":3477},{"class":169,"line":409},[3478],{"type":21,"tag":167,"props":3479,"children":3480},{"emptyLinePlaceholder":876},[3481],{"type":26,"value":879},{"type":21,"tag":167,"props":3483,"children":3484},{"class":169,"line":439},[3485,3489,3494,3498,3502,3506],{"type":21,"tag":167,"props":3486,"children":3487},{"style":305},[3488],{"type":26,"value":308},{"type":21,"tag":167,"props":3490,"children":3491},{"style":186},[3492],{"type":26,"value":3493}," captchaResult ",{"type":21,"tag":167,"props":3495,"children":3496},{"style":305},[3497],{"type":26,"value":1097},{"type":21,"tag":167,"props":3499,"children":3500},{"style":174},[3501],{"type":26,"value":323},{"type":21,"tag":167,"props":3503,"children":3504},{"style":180},[3505],{"type":26,"value":2772},{"type":21,"tag":167,"props":3507,"children":3508},{"style":186},[3509],{"type":26,"value":3510},"(token)\n",{"type":21,"tag":167,"props":3512,"children":3513},{"class":169,"line":478},[3514,3518,3523,3528,3532,3536,3540,3544,3549],{"type":21,"tag":167,"props":3515,"children":3516},{"style":174},[3517],{"type":26,"value":888},{"type":21,"tag":167,"props":3519,"children":3520},{"style":186},[3521],{"type":26,"value":3522}," (captchaResult ",{"type":21,"tag":167,"props":3524,"children":3525},{"style":305},[3526],{"type":26,"value":3527},"!==",{"type":21,"tag":167,"props":3529,"children":3530},{"style":336},[3531],{"type":26,"value":376},{"type":21,"tag":167,"props":3533,"children":3534},{"style":342},[3535],{"type":26,"value":3241},{"type":21,"tag":167,"props":3537,"children":3538},{"style":336},[3539],{"type":26,"value":339},{"type":21,"tag":167,"props":3541,"children":3542},{"style":186},[3543],{"type":26,"value":1062},{"type":21,"tag":167,"props":3545,"children":3546},{"style":174},[3547],{"type":26,"value":3548},"return",{"type":21,"tag":167,"props":3550,"children":3551},{"style":186},[3552],{"type":26,"value":3553}," captchaResult\n",{"type":21,"tag":167,"props":3555,"children":3556},{"class":169,"line":515},[3557],{"type":21,"tag":167,"props":3558,"children":3559},{"emptyLinePlaceholder":876},[3560],{"type":26,"value":879},{"type":21,"tag":167,"props":3562,"children":3563},{"class":169,"line":524},[3564,3568,3572],{"type":21,"tag":167,"props":3565,"children":3566},{"style":174},[3567],{"type":26,"value":1257},{"type":21,"tag":167,"props":3569,"children":3570},{"style":180},[3571],{"type":26,"value":183},{"type":21,"tag":167,"props":3573,"children":3574},{"style":186},[3575],{"type":26,"value":3576},"(email, firstName, lastName, message)\n",{"type":21,"tag":167,"props":3578,"children":3579},{"class":169,"line":559},[3580],{"type":21,"tag":167,"props":3581,"children":3582},{"style":186},[3583],{"type":26,"value":1274},{"type":21,"tag":83,"props":3585,"children":3587},{"id":3586},"deploying-to-cloudflare-pages",[3588],{"type":26,"value":3589},"Deploying to Cloudflare Pages",{"type":21,"tag":22,"props":3591,"children":3592},{},[3593],{"type":26,"value":3594},"Now I knew that Cloudflare Pages supported serverless functions and that Nuxt3 supported static site generation and serverless functions but it had its own syntax and I wasn't sure how it held together. The good news is it does, specifically:",{"type":21,"tag":3596,"props":3597,"children":3598},"ol",{},[3599,3621,3626,3631],{"type":21,"tag":3600,"props":3601,"children":3602},"li",{},[3603,3605,3611,3613,3619],{"type":26,"value":3604},"You have to use ",{"type":21,"tag":163,"props":3606,"children":3608},{"className":3607},[],[3609],{"type":26,"value":3610},"build",{"type":26,"value":3612}," and not ",{"type":21,"tag":163,"props":3614,"children":3616},{"className":3615},[],[3617],{"type":26,"value":3618},"generate",{"type":26,"value":3620}," or Nuxt will skip building the functions",{"type":21,"tag":3600,"props":3622,"children":3623},{},[3624],{"type":26,"value":3625},"Nuxt can convert the server/api folder into serverless functions",{"type":21,"tag":3600,"props":3627,"children":3628},{},[3629],{"type":26,"value":3630},"It uses a server called Nitro to do this",{"type":21,"tag":3600,"props":3632,"children":3633},{},[3634,3636],{"type":26,"value":3635},"Nitro supports multiple modes including ",{"type":21,"tag":51,"props":3637,"children":3640},{"href":3638,"rel":3639},"https://nitro.unjs.io/deploy/providers/cloudflare",[55],[3641],{"type":26,"value":3642},"three for Cloudflare alone",{"type":21,"tag":3644,"props":3645,"children":3646},"ul",{},[3647,3658,3677],{"type":21,"tag":3600,"props":3648,"children":3649},{},[3650,3656],{"type":21,"tag":163,"props":3651,"children":3653},{"className":3652},[],[3654],{"type":26,"value":3655},"cloudflare",{"type":26,"value":3657}," which uses the service-worker \"Cloudflare Worker\" syntax",{"type":21,"tag":3600,"props":3659,"children":3660},{},[3661,3667,3669,3675],{"type":21,"tag":163,"props":3662,"children":3664},{"className":3663},[],[3665],{"type":26,"value":3666},"cloudflare-pages",{"type":26,"value":3668}," (combining everything into a ",{"type":21,"tag":163,"props":3670,"children":3672},{"className":3671},[],[3673],{"type":26,"value":3674},"_worker.js",{"type":26,"value":3676}," file)",{"type":21,"tag":3600,"props":3678,"children":3679},{},[3680],{"type":21,"tag":163,"props":3681,"children":3683},{"className":3682},[],[3684],{"type":26,"value":3685},"cloudflare-module",{"type":21,"tag":22,"props":3687,"children":3688},{},[3689],{"type":26,"value":3690},"The docs say it should detect the environment and do the right thing but you can always override it in your nuxt.config.ts file (you'll also need to do this if you're wanting to check what it generates locally):",{"type":21,"tag":155,"props":3692,"children":3694},{"className":157,"code":3693,"language":159,"meta":160,"style":160},"// https://nuxt.com/docs/api/configuration/nuxt-config\nimport { defineNuxtConfig } from \"nuxt/config\"\n\nexport default defineNuxtConfig({\n  // ...\n  nitro: {\n    preset: \"cloudflare-pages\",\n  },\n  // ...\n})\n",[3695],{"type":21,"tag":163,"props":3696,"children":3697},{"__ignoreMap":160},[3698,3706,3735,3742,3762,3770,3786,3814,3821,3828],{"type":21,"tag":167,"props":3699,"children":3700},{"class":169,"line":170},[3701],{"type":21,"tag":167,"props":3702,"children":3703},{"style":1077},[3704],{"type":26,"value":3705},"// https://nuxt.com/docs/api/configuration/nuxt-config\n",{"type":21,"tag":167,"props":3707,"children":3708},{"class":169,"line":192},[3709,3713,3718,3722,3726,3731],{"type":21,"tag":167,"props":3710,"children":3711},{"style":1026},[3712],{"type":26,"value":1869},{"type":21,"tag":167,"props":3714,"children":3715},{"style":186},[3716],{"type":26,"value":3717}," { defineNuxtConfig } ",{"type":21,"tag":167,"props":3719,"children":3720},{"style":174},[3721],{"type":26,"value":1879},{"type":21,"tag":167,"props":3723,"children":3724},{"style":336},[3725],{"type":26,"value":376},{"type":21,"tag":167,"props":3727,"children":3728},{"style":342},[3729],{"type":26,"value":3730},"nuxt/config",{"type":21,"tag":167,"props":3732,"children":3733},{"style":336},[3734],{"type":26,"value":939},{"type":21,"tag":167,"props":3736,"children":3737},{"class":169,"line":219},[3738],{"type":21,"tag":167,"props":3739,"children":3740},{"emptyLinePlaceholder":876},[3741],{"type":26,"value":879},{"type":21,"tag":167,"props":3743,"children":3744},{"class":169,"line":240},[3745,3749,3753,3758],{"type":21,"tag":167,"props":3746,"children":3747},{"style":1026},[3748],{"type":26,"value":1029},{"type":21,"tag":167,"props":3750,"children":3751},{"style":174},[3752],{"type":26,"value":1034},{"type":21,"tag":167,"props":3754,"children":3755},{"style":180},[3756],{"type":26,"value":3757}," defineNuxtConfig",{"type":21,"tag":167,"props":3759,"children":3760},{"style":186},[3761],{"type":26,"value":556},{"type":21,"tag":167,"props":3763,"children":3764},{"class":169,"line":261},[3765],{"type":21,"tag":167,"props":3766,"children":3767},{"style":1077},[3768],{"type":26,"value":3769},"  // ...\n",{"type":21,"tag":167,"props":3771,"children":3772},{"class":169,"line":279},[3773,3778,3782],{"type":21,"tag":167,"props":3774,"children":3775},{"style":186},[3776],{"type":26,"value":3777},"  nitro",{"type":21,"tag":167,"props":3779,"children":3780},{"style":202},[3781],{"type":26,"value":205},{"type":21,"tag":167,"props":3783,"children":3784},{"style":186},[3785],{"type":26,"value":298},{"type":21,"tag":167,"props":3787,"children":3788},{"class":169,"line":301},[3789,3794,3798,3802,3806,3810],{"type":21,"tag":167,"props":3790,"children":3791},{"style":186},[3792],{"type":26,"value":3793},"    preset",{"type":21,"tag":167,"props":3795,"children":3796},{"style":202},[3797],{"type":26,"value":205},{"type":21,"tag":167,"props":3799,"children":3800},{"style":336},[3801],{"type":26,"value":376},{"type":21,"tag":167,"props":3803,"children":3804},{"style":342},[3805],{"type":26,"value":3666},{"type":21,"tag":167,"props":3807,"children":3808},{"style":336},[3809],{"type":26,"value":339},{"type":21,"tag":167,"props":3811,"children":3812},{"style":186},[3813],{"type":26,"value":216},{"type":21,"tag":167,"props":3815,"children":3816},{"class":169,"line":361},[3817],{"type":21,"tag":167,"props":3818,"children":3819},{"style":186},[3820],{"type":26,"value":1551},{"type":21,"tag":167,"props":3822,"children":3823},{"class":169,"line":392},[3824],{"type":21,"tag":167,"props":3825,"children":3826},{"style":1077},[3827],{"type":26,"value":3769},{"type":21,"tag":167,"props":3829,"children":3830},{"class":169,"line":409},[3831],{"type":21,"tag":167,"props":3832,"children":3833},{"style":186},[3834],{"type":26,"value":1274},{"type":21,"tag":22,"props":3836,"children":3837},{},[3838,3840,3845,3847,3853,3855,3860],{"type":26,"value":3839},"If you are using the Cloudflare Wrangler tool or GitHub actions to deploy then you'll probably need to search around the web for more information. If you're using the Cloudflare Pages UI then just make sure the tech stack is set for ",{"type":21,"tag":163,"props":3841,"children":3843},{"className":3842},[],[3844],{"type":26,"value":13},{"type":26,"value":3846}," and that it's using ",{"type":21,"tag":163,"props":3848,"children":3850},{"className":3849},[],[3851],{"type":26,"value":3852},"yarn build",{"type":26,"value":3854}," (you'll also need to set your environment variable for ",{"type":21,"tag":163,"props":3856,"children":3858},{"className":3857},[],[3859],{"type":26,"value":2992},{"type":26,"value":3861},").",{"type":21,"tag":83,"props":3863,"children":3865},{"id":3864},"final-testing",[3866],{"type":26,"value":3867},"Final testing",{"type":21,"tag":22,"props":3869,"children":3870},{},[3871],{"type":26,"value":3872},"Once that's all in place you should be able to go into the Cloudflare Pages UI and see that it now has functions detected. Head over to your contact form, fill in the details and hit send!",{"type":21,"tag":22,"props":3874,"children":3875},{},[3876],{"type":26,"value":3877},"If all goes well you should see the email come through and the result of the form submission should be \"ok\". If you get an error then check the console log for errors or the Brevo logs to see what happened.",{"type":21,"tag":83,"props":3879,"children":3881},{"id":3880},"conclusion",[3882],{"type":26,"value":3883},"Conclusion",{"type":21,"tag":22,"props":3885,"children":3886},{},[3887],{"type":26,"value":3888},"This was a fun little project to put together and I'm really happy with the result. I'm sure there are other ways to do this but this is a nice combination of free services that work well together and are easy to set up and maintain.",{"type":21,"tag":22,"props":3890,"children":3891},{},[3892],{"type":26,"value":3893},"I hope you found this useful!",{"type":21,"tag":22,"props":3895,"children":3896},{},[3897],{"type":26,"value":3898},"Damien",{"type":21,"tag":83,"props":3900,"children":3902},{"id":3901},"disclaimer",[3903],{"type":26,"value":3904},"Disclaimer",{"type":21,"tag":22,"props":3906,"children":3907},{},[3908],{"type":26,"value":3909},"I am a Brevo partner eligible for commission on sales I make directly with them however I do not receive any compensation for this article or have link-based referrals for commission.",{"type":21,"tag":3911,"props":3912,"children":3913},"style",{},[3914],{"type":26,"value":3915},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":160,"searchDepth":192,"depth":192,"links":3917},[3918,3919,3920,3923,3924,3925,3926,3927],{"id":85,"depth":192,"text":88},{"id":965,"depth":192,"text":968},{"id":1759,"depth":192,"text":1762,"children":3921},[3922],{"id":1799,"depth":219,"text":1802},{"id":2745,"depth":192,"text":2748},{"id":3586,"depth":192,"text":3589},{"id":3864,"depth":192,"text":3867},{"id":3880,"depth":192,"text":3883},{"id":3901,"depth":192,"text":3904},"markdown","content:blog:2023:send-email-with-nuxt3-cloudflare-and-brevo.md","content","blog/2023/send-email-with-nuxt3-cloudflare-and-brevo.md","blog/2023/send-email-with-nuxt3-cloudflare-and-brevo","md","/blog/2023/send-email-with-nuxt3-cloudflare-and-brevo/",1903,0,{"src":92,"alt":93,"width":94,"className":3938,"style":97},[96],[3940,3944,3948],{"title":3941,"date":3942,"url":3943},"HTML5 Video Cheatsheet: Optimizing videos for the web","2025-12-05T00:00:00Z","/blog/2025/html5-video-cheatsheet/",{"title":3945,"date":3946,"url":3947},"Transactions in the MongoDB EF Core Provider","2025-10-25","/blog/2025/mongodb-explicit-transactions/",{"title":3949,"date":3950,"url":3951},"Queryable Encryption with the MongoDB EF Core Provider","2025-09-22","/blog/2025/mongodb-queryable-encryption/",[],1779224631054]