Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 2561

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 26, 20242024-11-26T06:33:07+00:00 2024-11-26T06:33:07+00:00

How to create an API endpoint in astro

  • 61k
  • Introduction
  • What's the setup?
    • 1. Ready your Astro project
    • 2. SSR
    • 2.1 Adapter
    • 2.2 server or hybrid
    • 2.3 A note on environment variables
    • 2.4 A note on console.log
    • 3. File structure
  • The API endpoint
    • 1. Create the file
    • 2. The code
  • The form

Introduction

Gone are the days when you had to create a separate API server to serve your frontend application. With Astro, you can create API endpoints directly in your app. Which means you can even create a full-stack application with just one codebase.

Personally, I use these endpoints for simple actions such as:

  • Contact Forms
  • Newsletter subscriptions
  • User registration
  • Even user authentication sometimes

It's also useful for when you need to fetch and process data from a source that requires authentication (I.e. an API key) and you don't want to expose that key in the frontend.

In this article we'll go through the steps to create an API endpoint in Astro.

What's the setup?

In this article we'll create a simple API endpoint that creates a contact in Brevo, which is mostly what I use these endpoints for. You can replace this with any other service you want to interact with.

1. Ready your Astro project

If you're new to Astro or haven't set up an Astro project yet, I recommend you to check out the official documentation.

You can also start with one of their themes.

2. SSR

In order to have a working API endpoint that works at runtime, you need to enable SSR (Server Sider Rendering).

SSR allows you to have the app run on the server before it gets to the client.

2.1 Adapter

For this, you will need an adapter.

What an adapter does is it allows you to run your SSR Astro App in different environments. For example, you can run it in a serverless environment like Vercel or Netlify, or in a Node.js server.

You can see a list of official and community adapters here.

For this article, I'll use the @astrojs/adapter-node adapter since I host my side in a Node docker container.

Super easy to install:

npx astro add node 
Enter fullscreen mode Exit fullscreen mode

2.2 server or hybrid

Astro allows you to run SSR in 2 ways:

  • server: On-demand rendered by default. Basically uses the server for everything. Use this when most of your site should be dynamic. You can opt-out of SSR for individual pages or endpoints.

  • hybrid: Pre-rendered to HTML by default. It does not pre-render the page on the server. Use this when most of your site should be static. You can opt-in to SSR for individual pages or endpoints.

For my usecase where most of my site is static (it's a landing page after all) I use hybrid:

// astro.config.mjs  import { defineConfig } from "astro/config"; import node from "@astrojs/node";  export default defineConfig({   output: "hybrid",   adapter: node({     mode: "standalone",   }), }); 
Enter fullscreen mode Exit fullscreen mode

2.3 A note on environment variables

If you're used to Astro, you know that you can use environment variables by calling:

const MY_VARIABLE = import.meta.env.VARIABLE_NAME; 
Enter fullscreen mode Exit fullscreen mode

However, because Astro is static by default, what this really does is get the environment variable at build time. Then, the variable is hardcoded into the build code, which means that if you change the environment variable after the build, it won't be reflected in the code.

If you use SSR it works differently, import.meta.env won't work since it's available at build time but not at runtime on the server.

You will need to use process.env instead:

const MY_VARIABLE = process.env.VARIABLE_NAME; 
Enter fullscreen mode Exit fullscreen mode

BUT WAIT!

There's another catch, and that's that process.env is not available with npm run dev, which means your code will crash when you try to run it locally.

The solution:

const MY_VARIABLE = import.meta.env.VARIABLE_NAME ?? process.env.VARIABLE_NAME; 
Enter fullscreen mode Exit fullscreen mode

This code will try to get the environment variable from import.meta.env first, and if it's not available it will try to get it from process.env. This way, your code will work both in development and production.

2.4 A note on console.log

If you're used to using console.log to debug your code, you'll know that it will show up in the browser console when you're running the app in development mode.

When using console.log in an SSR component, because it runs on the server, the logs will show up in the terminal where you're running the app.

So if you're looking for your logs and can't find them, check the terminal where you're running the app.

3. File structure

The full functionality needs 2 files:

  • A .js or .ts API endpoint file that lives in the src/pages/api directory.
  • A form that gets the user input and sends it to the API endpoint. I personally like to do this in a .tsx file because I can then use the full power of react (react-hook-form and zod) to handle the form. Place this form wherever you like, I like having all my forms in src/components/forms.

That's pretty much it! The form will send the data to our API endpoint, which will then process it and send it to Brevo.


The API endpoint

1. Create the file

Let's create the API endpoint that will send the data to Brevo.

You can create this endpoint wherever you want under the src/pages/ directory depending on where you want it to be accessible.

For instance, I like my endpoints to be accessible under /api/ so I create a src/pages/api/ directory.

So my endpoint file will be src/pages/api/create-brevo-contact.ts.

This means that I will be able to access it at http://mydomain.com/api/create-brevo-contact.


2. The code

Your API endpoint code should have a pretty simple structure:

// SSR API endpoint template  // Tell Astro that this component should run on the server // You only need to specify this if you're using the hybrid output export const prerender = false;  // Import the APIRoute type from Astro import type { APIRoute } from "astro";  // This function will be called when the endpoint is hit with a GET request export const GET: APIRoute = async ({ request }) => {   // Do some stuff here    // Return a 200 status and a response to the frontend   return new Response(     JSON.stringify({       message: "Operation successful",     }),     {       status: 200,     }   ); }; 
Enter fullscreen mode Exit fullscreen mode

Following the template above, this is a simple POST API endpoint to create a contact in Brevo. Everything is commented so you can understand what's going on:

// src/pages/api/create-brevo-contact.ts  // Because I chose hybrid, I need to specify that this endpoint should run on the server: export const prerender = false;  // Import the APIRoute type from Astro import type { APIRoute } from "astro";  // This is the function that will be called when the endpoint is hit export const POST: APIRoute = async ({ request }) => {   // Check if the request is a JSON request   if (request.headers.get("content-type") === "application/json") {     // Get the body of the request     const body = await request.json();      // Get the email from the body     const email = body.email;      // Declares the Brevo API URL     const BREVO_API_URL = "https://api.brevo.com/v3/contacts";      // Gets the Brevo API Key from an environment variable     // Check the note on environment variables in the SSR section of this article to understand what is going on here     const BREVO_API_KEY =       import.meta.env.BREVO_API_KEY ?? process.env.BREVO_API_KEY;      // Just a simple check to make sure the API key is defined in an environment variable     if (!BREVO_API_KEY) {       console.error("No BREVO_API_KEY defined");       return new Response(null, { status: 400 });     }      // The payload that will be sent to Brevo     // This payload will create or update the contact and add it to the list with ID 3     const payload = {       updateEnabled: true,       email: email,       listIds: [3],     };      // Whatever process you want to do in your API endpoint should be inside a try/catch block     // In this case we're sending a POST request to Brevo     try {       // Make a POST request to Brevo       const response = await fetch(BREVO_API_URL, {         method: "POST",         headers: {           accept: "application/json",           "api-key": BREVO_API_KEY,           "content-type": "application/json",         },         body: JSON.stringify(payload),       });        // Check if the request was successful       if (response.ok) {         // Request succeeded         console.log("Contact added successfully");          // Return a 200 status and the response to our frontend         return new Response(           JSON.stringify({             message: "Contact added successfully",           }),           {             status: 200,           }         );       } else {         // Request failed         console.error("Failed to add contact to Brevo");          // Return a 400 status to our frontend         return new Response(null, { status: 400 });       }     } catch (error) {       // An error occurred while doing our API operation       console.error(         "An unexpected error occurred while adding contact:",         error       );        // Return a 400 status to our frontend       return new Response(null, { status: 400 });     }   }    // If the POST request is not a JSON request, return a 400 status to our frontend   return new Response(null, { status: 400 }); }; 
Enter fullscreen mode Exit fullscreen mode

That's it, you now have a working API endpoint that will create a contact in Brevo when hit with a POST request!


The form

As a bonus, I also want to show you how I code my forms to make them responsive.

For this example, I'll create a simple form, with only an email field and a submit button, that will send the email a user inputs to the API endpoint we created.

Here's the code:

Note that this code is using shadcn ui components for the HTML, you might need to replace them with your own components.

// WaitlistForm.tsx  // Zod validation stuff const WaitlistFormSchema = z.object({   email: z     .string()     .min(1, "Please enter a valid email")     .email("Please enter a valid email"), });  type WaitlistFormValues = z.infer<typeof WaitlistFormSchema>;  const WaitlistForm = () => {   // Hooks to check the status of the form   const [isSubmitting, setIsSubmitting] = useState(false);   const [isSuccess, setIsSuccess] = useState(false);   const [error, setError] = useState("");    // React Hook Form stuff   const form = useForm<WaitlistFormValues>({     resolver: zodResolver(WaitlistFormSchema),     defaultValues: {       email: "",     },   });    // Function that sends the data to the API endpoint when the form is submitted   const onSubmit = async (data: WaitlistFormValues) => {     setIsSubmitting(true);      // Ping out API endpoint     const response = await fetch("/api/create-brevo-contact", {       method: "POST",       headers: {         "content-type": "application/json",       },       body: JSON.stringify(data),     });      // If successful, reset the form and show a success message     if (response.ok) {       form.reset();       setIsSuccess(true);     } else {       // If failed, show error message       console.error("Failed to add contact");       setIsSuccess(false);       setError("There's been an error. Please try again.");     }      setIsSubmitting(false);   };    return (     <>       {isSuccess && (         <Alert className="mb-3 md:mb-8 bg-green-100 border-green-300">           <AlertTitle>Thanks!</AlertTitle>           <AlertDescription>             We've added you to the waitlist!             <br />           </AlertDescription>         </Alert>       )}        {!isSuccess && error && (         <Alert className="mb-8 bg-red-100 border-red-300">           <AlertTitle>Error</AlertTitle>           <AlertDescription>{error}</AlertDescription>         </Alert>       )}        <Form {...form}>         <form           onSubmit={form.handleSubmit(onSubmit)}           className="space-y-4 md:space-y-8"         >           <FormField             control={form.control}             name="email"             render={({ field }) => (               <FormItem>                 <FormLabel>Email</FormLabel>                 <FormControl>                   <Input                     className="bg-transparent"                     placeholder="email@gmail.com"                     {...field}                   />                 </FormControl>                 <FormMessage />               </FormItem>             )}           />            <Button type="submit" disabled={isSubmitting}>             <Loader2               className={`w-6 h-6 mr-2 animate-spin ${                 isSubmitting ? "block" : "hidden"               }`}             />             Submit           </Button>         </form>       </Form>     </>   ); };  export default WaitlistForm; 
Enter fullscreen mode Exit fullscreen mode

astrojavascripttypescriptwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 1k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.