<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts tagged #serverless</title><description>All posts tagged with serverless on queen.raae.codes</description><link>https://queen.raae.codes/tag/serverless/</link><item><title>📝 ✨ ~ Failing to use NextAuth with a Xata Worker for Prune your Follows</title><link>https://queen.raae.codes/2023-01-20-nextauth-xata-fail/</link><guid isPermaLink="true">https://queen.raae.codes/2023-01-20-nextauth-xata-fail/</guid><description>I naively thought all we needed for NextAuth to work with a Xata Worker was getting access to the NEXTAUTHSECRET env variable. But alas, when moving to a Xata…</description><pubDate>Fri, 20 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I naively thought all we needed for NextAuth to work with a Xata Worker was getting access to the &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt; env variable.&lt;/p&gt;
&lt;p&gt;But alas, when moving to a Xata Worker, the cookie set by NextAuth is unavailable as the Worker is served from another domain. However, the &lt;code&gt;getToken&lt;/code&gt; function can also get the token from the `Authorization: &apos;Bearer token&apos;. But how do we make that happen 🤷‍♀️&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/raae/status/1616165782123069459&quot;&gt;&lt;img src=&quot;./tweet.png&quot; alt=&quot;That feeling when you realise on stream that your cookie + JWT knowledge is not as solid as you thought...well it will become solid!&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We&apos;ll figure it out over the next few weeks! I am sure 💪&lt;/p&gt;
&lt;p&gt;If you have any experience with Cloudflare Workers (Xata Workers are an abstraction on top of Cloudflare Workers) and NextAuth, please reach out 🙏&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;br/&gt;&lt;ul&gt;&lt;li&gt;Queen Raae and family worked on several projects for Xata.&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title>📝 ✨ ~ Exploring Xata (Cloudflare) Workers for Prune your Follows</title><link>https://queen.raae.codes/2023-01-13-xata-workers-part-1/</link><guid isPermaLink="true">https://queen.raae.codes/2023-01-13-xata-workers-part-1/</guid><description>As soon as Prune your Follows facilitates 500 unfollows in 24 hours, it gets blocked by Twitter for 24 hours. To combat this, we think that some type of…</description><pubDate>Fri, 13 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As soon as Prune your Follows facilitates 500 unfollows in 24 hours, it gets blocked by Twitter for 24 hours.&lt;/p&gt;
&lt;p&gt;To combat this, we think that some type of queuing architecture is needed. Feel free to reply if you have any input!&lt;/p&gt;
&lt;p&gt;So we decided to explore Xata Workers on this week&apos;s &lt;a href=&quot;https://youtu.be/O89C_yxZK3o&quot;&gt;unauthorized and rum-fueled treasure hunt&lt;/a&gt; as Xata Workers is built on top of &lt;a href=&quot;https://developers.cloudflare.com/workers&quot;&gt;Cloudflare Workers&lt;/a&gt; and Cloudflare also has &lt;a href=&quot;https://developers.cloudflare.com/queues/&quot;&gt;Cloudflare Queues&lt;/a&gt; 🤔&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/O89C_yxZK3o&quot;&gt;&lt;img src=&quot;./screengrab.jpg&quot; alt=&quot;Screengrab of stream with crazy looking Captain Ola and funny looking Pirate Princess&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To my surprise &lt;a href=&quot;https://youtu.be/O89C_yxZK3o?t=1558&quot;&gt;we did get a Worker working&lt;/a&gt; locally while on stream 🥳&lt;/p&gt;
&lt;p&gt;However, we did not handle authenticating the user inside our Xata Worker. Needed to make sure the user has access and only gets relevant records.&lt;/p&gt;
&lt;p&gt;Pirate Shai to the rescue; hopefully, time will tell...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/shaisc/status/1613679030494023682&quot;&gt;&lt;img src=&quot;./twitter.com_shaisc.png&quot; alt=&quot;getToken() is a shortcut wrapper that uses process.env, which doesn&apos;t exist in the Xata Worker, but you can instead use decode() directly and pass it the token (from request.headers) and secret (from env) yourself&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The How&lt;/h2&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;We initialized Xata Workers in our exiting Xata project &lt;code&gt;xata workers init&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;defined a Xata Worker, copy/past-ing the code from our Gatsby Function,&lt;/li&gt;
&lt;li&gt;and then used the defined Worker directly in our &lt;code&gt;useQuery&lt;/code&gt; replacing the GET request to &lt;code&gt;/api/account&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Xata CLI will magically cut out the Xata Worker code and deploy it to Cloudflare for us, using &lt;code&gt;xata upload&lt;/code&gt; 🤯&lt;/p&gt;
&lt;p&gt;For local development, we used &lt;code&gt;xata workers watch&lt;/code&gt;. Not completly sure what happens then 😬&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useQuery } from &amp;quot;@tanstack/react-query&amp;quot;;
import { useUser } from &amp;quot;../user&amp;quot;;

import { xataWorker } from &amp;quot;./../../xata&amp;quot;;

// Define Xata Worker 1️⃣
const searchAccount = xataWorker(
  &amp;quot;searchAccount&amp;quot;,
  async ({ xata }, { search }) =&amp;gt; {
    const searchResults = await xata.search.all(search, {
      tables: [
        {
          table: &amp;quot;accounts&amp;quot;,
          target: [
            { column: &amp;quot;name&amp;quot;, weight: 3 },
            { column: &amp;quot;username&amp;quot;, weight: 7 },
            { column: &amp;quot;meta.location&amp;quot; },
            { column: &amp;quot;meta.description&amp;quot; },
          ],
        },
      ],
      highlight: { enabled: true },
      fuzziness: 1,
      prefix: &amp;quot;phrase&amp;quot;,
    });

    const records = searchResults.map((result) =&amp;gt; {
      return {
        ...result,
        searchInfo: result.record.getMetadata(),
      };
    });

    return { records };
  }
);

export default function useSearch({ search }) {
  const { data: user } = useUser();

  return useQuery(
    [&amp;quot;accounts&amp;quot;, user?.id, &amp;quot;search&amp;quot;, search],
    async () =&amp;gt; {
      // Use the Xata Worker 2️⃣
      return searchAccount({ search: search });
    },
    {
      enabled: Boolean(user?.enableQueries) &amp;amp;&amp;amp; Boolean(search),
      keepPreviousData: true,
    }
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned, this code is incomplete, as we need to filter the accounts by the authenticated user.&lt;/p&gt;
&lt;p&gt;Sign up for an &lt;a href=&quot;https://queen.raae.codes/emails/reminders/&quot;&gt;email reminder&lt;/a&gt; to ensure you catch the next stream, where we&apos;ll try to make that happen.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;p&gt;PS: Follow the &lt;a href=&quot;https://github.com/queen-raae/prune-your-follows/pull/74&quot;&gt;Pull Request&lt;/a&gt; if you are very interested 🤪&lt;/p&gt;
&lt;br/&gt;&lt;ul&gt;&lt;li&gt;Queen Raae and family worked on several projects for Xata.&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title>🧪 📺 ~ How to TDD your Gatsby (Serverless) Functions with Jest</title><link>https://queen.raae.codes/2022-06-14-tdd-functions/</link><guid isPermaLink="true">https://queen.raae.codes/2022-06-14-tdd-functions/</guid><description>Interested in getting started with Test Driven Development? Mirjam Diala (@mirjamdiala) show us how in last summers Testing your Gatsby Serverless Functions…</description><pubDate>Tue, 14 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Interested in getting started with Test Driven Development?&lt;/p&gt;
&lt;p&gt;Mirjam Diala (&lt;a href=&quot;https://twitter.com/mirjam_diala&quot;&gt;@mirjam_diala&lt;/a&gt;) show us how in last summers &lt;a href=&quot;https://www.crowdcast.io/e/testing-your-functions&quot;&gt;Testing your Gatsby Serverless Functions&lt;/a&gt; webinar.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.crowdcast.io/e/testing-your-functions&quot;&gt;&lt;img src=&quot;./screengrab-crowdcast.jpg&quot; alt=&quot;&amp;quot;Screengrab of webinar&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We get into the TDD mentality and showcase how to specifically use Jest to test your Gatsby Serverless Functions.&lt;/p&gt;
&lt;p&gt;The code can be found on &lt;a href=&quot;https://github.com/queen-raae/webinar-functions-testing&quot;&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>🏷 🪝 ~ How to trigger a Stripe (checkout.session.completed) Event with metadata</title><link>https://queen.raae.codes/2022-06-03-stripe-cli/</link><guid isPermaLink="true">https://queen.raae.codes/2022-06-03-stripe-cli/</guid><description>We had fun with the Stripe CLI in yesterday&apos;s unauthorized and rum-fueled treasure hunt in the sharky waters around the Gatsby islands while refactoring the…</description><pubDate>Fri, 03 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We had fun with the &lt;a href=&quot;https://stripe.com/docs/cli&quot;&gt;Stripe CLI&lt;/a&gt; in yesterday&apos;s &lt;a href=&quot;https://youtu.be/Wqilgl_V7FA&quot;&gt;unauthorized and rum-fueled treasure hunt&lt;/a&gt; in the sharky waters around the Gatsby islands while refactoring the Stripe Webhook of our price-winning &lt;a href=&quot;https://github.com/queen-raae/gatsby-funcjam-21/pull/4&quot;&gt;Gatsby FuncJam entry&lt;/a&gt; to use the raw body for Stripe verification.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/Wqilgl_V7FA&quot;&gt;&lt;img src=&quot;./youtube-screengrab_lol.jpg&quot; alt=&quot;YouTube Screengrab of crazy looking queen&quot; title=&quot;Caption this 🤪&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The What?&lt;/h2&gt;
&lt;p&gt;Trigger Stripe Checkout Session with metadata&lt;/p&gt;
&lt;h2&gt;The Why?&lt;/h2&gt;
&lt;p&gt;Sometimes your Stripe Webhook expects there to be metadata attached to the Checkout Session. In our case, we want to grant access to a private Github repository and therefore add the Github username to the Checkout Session.&lt;/p&gt;
&lt;h2&gt;The How&lt;/h2&gt;
&lt;p&gt;You can add or override the data on resources when triggering events using &lt;a href=&quot;https://stripe.com/docs/cli/trigger#trigger-add&quot;&gt;the add flag&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The documentation lets us know:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;--add [resource]:[path1].[path2]=[value]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add metadata to a Stripe Checkout Session, &lt;code&gt;resource&lt;/code&gt; should be swapped for &amp;quot;checkout_session &lt;code&gt;path1&lt;/code&gt; for &amp;quot;metadata&amp;quot; and &lt;code&gt;path2&lt;/code&gt; for your custom metadata key. In our case, &amp;quot;github&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;--add checkout_session:metadata.github=olavea
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we use the above when triggering events, such as &amp;quot;checkout.session.completed&amp;quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;stripe trigger checkout.session.completed --add checkout_session:metadata.github=olavea
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 
I had to upgrade my Stripe CLI to get this functionality, so I believe this is a new addition in the last year or so.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>👩‍🏫 📺 ~ A practical introduction to Gatsby (Serverless) Functions</title><link>https://queen.raae.codes/2022-06-02-gatsby-functions/</link><guid isPermaLink="true">https://queen.raae.codes/2022-06-02-gatsby-functions/</guid><description>Ready to get going with Serverless Functions? Check out the A practical introduction to Gatsby (Serverless) Functions webinar that kick-started last years…</description><pubDate>Thu, 02 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ready to get going with Serverless Functions?&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://www.crowdcast.io/e/a-practical-introduction?utm_source=profile&amp;amp;utm_medium=profile_web&amp;amp;utm_campaign=profile&quot;&gt;A practical introduction to Gatsby (Serverless) Functions&lt;/a&gt; webinar that kick-started last years &lt;a href=&quot;https://www.youtube.com/playlist?list=PL9W-8hhRoLoO79t6nLx36tNsLyBAowbHT&quot;&gt;Gatsby Summer Functions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.crowdcast.io/e/a-practical-introduction?utm_source=profile&amp;amp;utm_medium=profile_web&amp;amp;utm_campaign=profile&quot;&gt;&lt;img src=&quot;./screengrab-crowdcast.jpg&quot; alt=&quot;&amp;quot;Screengrab of webinar&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
&lt;strong&gt;PS:&lt;/strong&gt; On tonight&apos;s &lt;a href=&quot;https://youtu.be/Wqilgl_V7FA&quot;&gt;unauthorized and rum-fueled treasure hunt&lt;/a&gt;, we&apos;ll refactor the webhook from our price-winning &lt;a href=&quot;https://github.com/queen-raae/gatsby-funcjam-21/blob/main/src/api/stripe-webhook.js&quot;&gt;Gatsby FuncJam entry&lt;/a&gt; to use the raw body for Stripe verification.&lt;/p&gt;
</content:encoded></item><item><title>λ 🎉 ~ Raw body works in develop from v4.16</title><link>https://queen.raae.codes/2022-06-01-raw-body-fix/</link><guid isPermaLink="true">https://queen.raae.codes/2022-06-01-raw-body-fix/</guid><description>As mentioned in Stripe Event validation in a Gatsby Serverless Function, getting the raw body in a Gatsby Serverless Functions did not work in development.…</description><pubDate>Wed, 01 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As mentioned in &lt;a href=&quot;/2022-05-19-stripe-verification/&quot;&gt;Stripe Event validation in a Gatsby Serverless Function&lt;/a&gt;, getting the raw body in a Gatsby Serverless Functions did not work in development.&lt;/p&gt;
&lt;p&gt;Luckily the framework team at Gatsby agreed it was a bug and fixed it. It looks like it will ship with Gatsby v4.16.&lt;/p&gt;
&lt;p&gt;I got it working today using 4.16.0-next.1.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn add gatsby@4.16.0-next.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>λ 💰 ~ Stripe Event validation in a Gatsby Serverless Function</title><link>https://queen.raae.codes/2022-05-19-stripe-verification/</link><guid isPermaLink="true">https://queen.raae.codes/2022-05-19-stripe-verification/</guid><description>If you are new to serverless, you might have thought nothing of yesterday&apos;s &quot;Using Http Request Body&quot; example. js export default function handler(req, res) {…</description><pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you are new to serverless, you might have thought nothing of &lt;a href=&quot;/2022-05-18-dynamic-serverless/&quot;&gt;yesterday&apos;s &amp;quot;Using Http Request Body&amp;quot; example&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default function handler(req, res) {
  const { city, year } = req.body;
  res.send(`You time-travelled to ${city}, in year ${year}`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But having the body served to you automagically as json, when it makes sense, is something I really like about Gatsby Functions. In most other serverless implementations, you have to parse it yourself.&lt;/p&gt;
&lt;p&gt;However, when it comes to verifying the signature of a webhook using a third-party SDK, not so much! Most signature checks require access to the raw body.&lt;/p&gt;
&lt;p&gt;And for the longest time, we could not access the raw body in a Gatsby Function, but with the release of Gatbsy v4.14, we can 🎉&lt;/p&gt;
&lt;p&gt;To control the &lt;code&gt;body-parser&lt;/code&gt; middleware export an &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/functions/middleware-and-helpers/#accessing-body-as-a-buffer&quot;&gt;object named &lt;code&gt;config&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const stripe = require(&amp;quot;stripe&amp;quot;)(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_ENDPOINT_SECRET;

// Export a config object
// to configure the bodyParser
export const config = {
  bodyParser: {
    raw: {
      type: `*/*`,
    },
  },
};

export default function handler(req, res) {
  try {
    const sig = req.headers[&amp;quot;stripe-signature&amp;quot;];
    const event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);

    // Handle the verified event,
    // event data can be trusted
    switch (event.type) {
      case &amp;quot;checkout.session.completed&amp;quot;:
        console.log(&amp;quot;CheckoutSession completed, fulfill the order!&amp;quot;);
        // Handle the event
        break;
      // ... handle other event types
      default:
        console.log(`Unhandled event type ${event.type}`);
    }

    res.status(200);
  } catch (err) {
    console.warn(err.message);
    res.status(400);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;s&gt;Unfortunately, I cannot seem to make this work locally, but I have verified that it works on Gatsby Cloud. I will, of course, follow up with Gatsby to figure out what&apos;s up. Is it a feature? Or a bug? I know my opinion on the matter 😬&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;Update: &lt;a href=&quot;/2022-06-01-raw-body-fix/&quot;&gt;Raw body works in develop from v4.16&lt;/a&gt;&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
</content:encoded></item><item><title>3️⃣ λ ~ Dynamic data in your Gatsby Serverless Functions</title><link>https://queen.raae.codes/2022-05-18-dynamic-serverless/</link><guid isPermaLink="true">https://queen.raae.codes/2022-05-18-dynamic-serverless/</guid><description>How do you get data into a serverless function? There are three common approaches: 1. Using URL Queries Demo - /api/time-travel-query?city=oslo&amp;year=1624…</description><pubDate>Wed, 18 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;How do you get data into a serverless function?&lt;/p&gt;
&lt;p&gt;There are three common approaches:&lt;/p&gt;
&lt;h2&gt;1. Using URL Queries&lt;/h2&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://demodatafunctions.gatsbyjs.io/api/time-travel-query?city=oslo&amp;amp;year=1624&quot;&gt;/api/time-travel-query?city=oslo&amp;amp;year=1624&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Documentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Gatsby Documentation on &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/functions/middleware-and-helpers#data-formats&quot;&gt;Data formats (including URL Queries)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Wikipedia article on &lt;a href=&quot;https://en.wikipedia.org/wiki/Query_string&quot;&gt;Query Strings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Serverless Function Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// File: /api/time-travel-query
// Usage: /api/time-travel-query?city=oslo&amp;amp;year=1624
export default function handler(req, res) {
  const { city, year } = req.query;
  res.send(`You time-travelled to ${city}, in year ${year}`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Using Param Routes&lt;/h2&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://demodatafunctions.gatsbyjs.io/api/time-travel-params/oslo/1624&quot;&gt;/api/time-travel-params/oslo/1624&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Documentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Gatsby Documentation on &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/functions/routing/#dynamic-routing&quot;&gt;Dynamic routing (including Param Routes)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Serverless Function Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// File: /api/time-travel-params/[city]/[year].js
// Usage: /api/time-travel-params/oslo/1624
export default function handler(req, res) {
  const { city, year } = req.params;
  res.send(`You time-travelled to ${city}, in year ${year}`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Using Http Request Body&lt;/h2&gt;
&lt;h3&gt;Demo&lt;/h3&gt;
&lt;p&gt;&amp;lt;form action=&amp;quot;https://demodatafunctions.gatsbyjs.io/api/time-travel-body&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;
&amp;lt;label htmlFor=&amp;quot;year&amp;quot;&amp;gt;Year: &amp;lt;/label&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;input
required
type=&amp;quot;number&amp;quot;
id=&amp;quot;year&amp;quot;
name=&amp;quot;year&amp;quot;
value=&amp;quot;1624&amp;quot;
/&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
&amp;lt;label htmlFor=&amp;quot;city&amp;quot;&amp;gt;City: &amp;lt;/label&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;input
required
type=&amp;quot;text&amp;quot;
id=&amp;quot;city&amp;quot;
name=&amp;quot;city&amp;quot;
value=&amp;quot;Oslo&amp;quot;
/&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;button&amp;gt;Travel&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/p&gt;
&lt;h3&gt;Documentation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Gatsby Documentation on &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/functions/middleware-and-helpers#data-formats&quot;&gt;Data formats (including body params)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Serverless Function Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// File: /api/time-travel-body.js
// Usage: {city: &amp;quot;Oslo&amp;quot;, year: &amp;quot;2026&amp;quot;} added to request body
export default function handler(req, res) {
  const { city, year } = req.body;
  res.send(`You time-travelled to ${city}, in year ${year}`);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the full demo code on &lt;a href=&quot;https://github.com/queen-raae/demo-data-functions&quot;&gt;GitHub&lt;/a&gt; or take it for a spin using &lt;a href=&quot;https://codesandbox.io/s/demo-data-functions-e9gtq&quot;&gt;CodeSandbox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;br&gt;
All the best,&lt;br&gt;
Queen Raae&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; Yesterday was May 17th, a special day in Norway. &lt;a href=&quot;https://twitter.com/raae/status/1526811316349984768&quot;&gt;Check out the reason and the fam in our finest on Twitter&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item></channel></rss>