Skip to main content

Build Your Own Blog With Astro & Altogic

· 12 min read
Emre Bayrak

Astro is a recently popular web development technology. It's a multi-page application (MPA) framework that allows apps to load with no JavaScript, providing the benefits of modern frameworks like Next.js, but with the performance of traditional MPAs. Its "islands architecture" lets you choose when to load your JavaScript code. Additionally, Astro enables you to use multiple frameworks within a single application, making it a highly versatile option for web developers.

Setting up Development Environment

To complete this tutorial, ensure you have installed the following tools and utilities on your local development environment.

Creating an Altogic Account

After creating an account, you will see the workspace and repository page. You can create a new app by clicking the + New app button.

https://www.altogic.com/blog/img/blog/2022-05-12/New_app.png

Click + New app and follow the instructions;

  1. In the App name field, enter a name for the app.
  2. Enter your subdomain.
  3. Choose the deployment location.
  4. And select your free execution environment pricing plan.

https://www.altogic.com/blog/img/blog/2022-05-12/todo_app.png

For this tutorial, we will use the blank template. So, select the Blank template and click Next.

note

In the template section, you can choose the template you want to use. Basic template creates a default user data model for your app, which is required by Altogic Client Library to store user data and manage authentication. If you don't need authentication, you can use the Blank template.

Later, if you want, you can easily create a new data model manually and from the App Settings → Authentication mark this new data model as your user data model.

https://www.altogic.com/blog/img/blog/2022-05-12/Screenshot_2023-01-19_at_14.11.35.png

Awesome! We have created our application; Now click/tap on the newly created app to launch the Designer. In order to access the app and use the Altogic client library, we should get envUrl and clientKey of this app.

Click the Home section in the left menu and copy the API base URL and Master client key for your app.

https://www.altogic.com/blog/img/blog/2022-05-12/home.png

info

In this tutorial we don't need authentication. So, let's disable session based authentication from the App Settings → Client library keys → Master client key view of Altogic Designer.

https://www.altogic.com/blog/img/blog/2022-05-12/Screenshot_2023-01-19_at_16.35.14.png

By clicking the Enforce session checkbox, you can disable session based authentication. Now, we are ready to start building our app

Creating an Astro Project

Open your terminal and navigate to the directory where you want to create your project. Then run the following command to create an Astro project.

npm create astro@latest

Installing React into our Astro Project

After creating our Astro Project, we need to install ReactJs into it to use react components and hooks.

# Using NPM
npx astro add react
# Using Yarn
yarn astro add react
# Using PNPM
pnpm astro add react

Installing Tailwind into our Astro Project

# Using NPM
npx astro add tailwind
# Using Yarn
yarn astro add tailwind
# Using PNPM
pnpm astro add tailwind

Creating .env File, and Astro Settings

In Astro, .env file variables must start with PUBLIC to access from client side of the application. If you want to initiliaze your Altogic instance in a file, you must put a PUBLIC prefix to your env variables.


PUBLIC_ALTOGIC_ENV_URL=YOUR_ENV_URL
PUBLIC_ALTOGIC_CLIENT_KEY=YOUR_CLIENT_KEY

Also, for server-side rendering and building non-static site we must declare that it in our astro.config.mjs file.

import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";

// https://astro.build/config
import react from "@astrojs/react";

// https://astro.build/config
import tailwind from "@astrojs/tailwind";

// https://astro.build/config
export default defineConfig({
site: "https://example.com",
integrations: [mdx(), sitemap(), react(), tailwind()],
output: "server", // This must be declared
});

Creating the Backend

As we mentioned earlier, we will use Altogic to create the backend for our app. Altogic is a serverless backend as a service platform that provides authentication, database, and other services. You can create a free account on Altogic and create a new app.

Creating Blog Model

  1. Click/tap on Models on the left sidebar.
  2. Click New on the right of the screen and select model.
  3. Set model name as blog.
  4. Ensure that the Enable timestamps is selected.
  5. Click Next
New app

With using Altogic you can automatically generate Rest API for your models. You can also create custom API endpoints.

New app

We have created a blog model, now we need to add fields for to our model.

  • _id

    Unique object id

  • content

    Blog content in text

  • createdAt

    Data created time

  • updatedAt

    Data last updated time

  • title

    Blog title

  • coverImage

    Image URL of blog’s cover image

  • slug

    Blog slug ( Generated by title and index field with “Default value expression” )

  • index

    Unique index number for slug

To create unique slug’s you can use Altogic’s “Default value expression”. You can generate unique slugs with unique id’s with the following function.

Default value expression for slug
REPLACE(
REPLACE(TOLOWER(IF(this.title, this.title, "story")), " ", "-"),
"?",
""
) +
"-" +
SUM(this.index, 14152);
New app

Integrating Frontend with Backend

Now we have a basic UI for our app and a database design. Let’s integrate our Astro app with the backend. We will use Altogic Client Library to integrate our frontend with the backend.

Installing Altogic Client Library

You can install the Altogic Client Library with using npm or yarn. Open your project directory and run the following command to install the Altogic Client Library.

# using npm
npm install altogic
# OR is using yarn
yarn add altogic

Let’s create a utils/ folder in the root of the project directory and create a altogic.ts file in the utils/ folder.

Open altogic.ts and paste below code block to export the altogic client instance

After finishing our settings, we can create our Altogic instance and prepare for CRUD operations.

altogic.ts

import { createClient } from "altogic";

const altogic = createClient(
import.meta.env.PUBLIC_ALTOGIC_ENV_URL,
import.meta.env.PUBLIC_ALTOGIC_CLIENT_KEY
);
export const { db, auth, storage, endpoint, queue, realtime, cache } = altogic;

NOTE

You can find your envUrl and clientKey in the Home or Settings view of your app. You can also create a new environment and get the envUrl and clientKey for the new environment.

Disabling Session Requirement for Altogic Client Library

Since there are no users in this application, we need to disable Altogic Client Library’s session requirement.

To disable session requirement:

1- Go to Settings on Your Altogic Designer

You can find the settings on your Altogic Designer’s side menu on the left.

New app

2- Select Client Library Keys

When you open the page, a menu will be appeared on the left. Click on Client library keys on the left menu.

New app

3- Click on Your Active Key

Click on your active key. By default it’s Master client key.

New app

4- Disable Enforce Session

When you disable your enforce session and save your settings, you can use all db operations without session requirement.

New app

Creating React Components in .astro Files

After installing the necessary packages and configuring our settings, we can create our .jsx extension React components. After creating the component that lists our blogs with React, as shown below, we call it in our index.astro file. When calling it, the point we need to pay attention to is to provide the component with the prop client:load If we don't provide this prop, our functions inside the component will not work properly.

index.astro
---
import BaseHead from "../components/BaseHead.astro";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
import HeroSection from "../sections/HeroSection.astro";
import BlogList from "../components/BlogList";
---

<!DOCTYPE html>
<html lang="en">
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head>
<body>
<Header />

<HeroSection />
<div class="flex-col gap-10 px-10 text-center w-full flex justify-center items-center">
<h2 class="font-bold text-center text-[35px] w-full">From the blog</h2>
<p class="w-[60%] text-center py-5 text-gray-500">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</p>
<BlogList client:load />
</div>

<Footer />
</body>
</html>
BlogList.jsx
import { useEffect, useState } from "react";
import BlogCard from "./BlogCard.jsx";
import { db } from "../utils/altogic.ts";

export default function BlogList() {
const [blogs, setBlogs] = useState([]);

const getBlogs = async () => {
return await db.model("blog").get();
};

useEffect(() => {
getBlogs().then((blogs) => {
setBlogs(blogs.data);
});
}, []);

return (
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 items-center w-full justify-center gap-14 text-start mb-20">
{blogs.length > 0 && blogs.map((blog) => <BlogCard blog={blog} />)}
</div>
);
}

Creating a Blog

At first to create a blog, we first need to build a page with astro.

write-a-story.astro
---
import BaseHead from "../components/BaseHead.astro";
import CreatePost from "../components/CreatePost";
import Header from "../components/Header.astro";
---

<html lang="en">
<head>
<BaseHead
title={"Write a Story"}
description={"You can create a blog here"}
/>
</head>
<body>
<Header />

<div
class="w-full h-[100h] flex flex-col items-center justify-center py-20"
>
<CreatePost client:load />
</div>
</body>
</html>

After we create our .astro page, we can implement a React component inside it. Astro allows us to implement different components with different frameworks into one page.

CreatePost.jsx
import { useEffect, useState } from "react";
import { endpoint, storage } from "../utils/altogic.ts";
import { toast } from "react-toastify";

export default function CreatePost() {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [loading, setLoading] = useState(false);
const [file, setFile] = useState();
const [imagePreview, setImagePreview] = useState();
const handleUpload = () => {
document.getElementById("fileUpload").click();
};

const createStory = async (e) => {
e.preventDefault();
setLoading(true);

const { data } = await uploadImage(title, file);
if (data) {
await endpoint
.post("/blog", {
title,
content,
coverImage: data.publicPath,
})
.then((res) => {
if (res) {
alert("Blog Created");
setFile(null);
setTitle("");
setContent("");
}
});
}
};

const uploadImage = async (fileName, file) => {
return await storage.bucket("root").upload(fileName, file);
};

useEffect(() => {
if (file) {
setImagePreview(URL.createObjectURL(file));
}
}, [file]);
return (
<form
class="w-100 h-100 shadow-lg flex flex-col gap-5 p-10"
onSubmit={createStory}
>
<input
onChange={(e) => setFile(e.target.files[0])}
id="fileUpload"
type="file"
hidden
/>

<div
onClick={handleUpload}
class=" cursor-pointer w-full h-48 border-gray-200 border-2 flex justify-center items-center text-gray-600 "
>
{file ? (
<img
class="w-96 h-full overflow-hidden object-cover"
src={imagePreview}
></img>
) : (
<span>Add Image</span>
)}
</div>
<span>Story Title</span>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
class="w-96 border-2 h-10 px-2"
type="text"
/>
<span>Story Content</span>

<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
class="w-96 border-2 h-80 resize-none p-2"
></textarea>

<button
disabled={loading}
type="submit"
class="w-full bg-blue-500 h-10 text-white"
>
{loading ? "Loading..." : "Publish"}
</button>
</form>
);
}

To create a blog, we need three fields: title, content and coverImage. Our blog slug will be generated automatically based on title.

New app

After the user fills the input fields and publishes the blog, the cover image will first be stored in the Altogic store, and when the public path is returned a, data will be created according to the blog model we mentioned above with create endpoint, which Altogic Client Library provides.

Displaying Blogs

New app

Altogic Client Library provides different ways of data fetching. For displaying all the blogs, we will use db feature.

At first we need to build our landing page with astro. After we create basic fields, we can implement our React component, where we will fetch and display our data.

index.astro
---
import BaseHead from "../components/BaseHead.astro";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
import HeroSection from "../sections/HeroSection.astro";
import BlogList from "../components/BlogList";
---

<!DOCTYPE html>
<html lang="en">
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head>
<body>
<Header />

<HeroSection />
<div class="flex-col gap-10 px-10 text-center w-full flex justify-center items-center">
<h2 class="font-bold text-center text-[35px] w-full">From the blog</h2>
<p class="w-[60%] text-center py-5 text-gray-500">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</p>
<BlogList client:load />
</div>

<Footer />
</body>
</html>
BlogList.jsx
import { useEffect, useState } from "react";
import BlogCard from "./BlogCard.jsx";
import { db } from "../utils/altogic.ts";

export default function BlogList() {
const [blogs, setBlogs] = useState([]);

const getBlogs = async () => {
return await db.model("blog").get();
};

useEffect(() => {
getBlogs().then((blogs) => {
setBlogs(blogs.data);
});
}, []);

return (
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 items-center w-full justify-center gap-14 text-start mb-20">
{blogs.length > 0 && blogs.map((blog) => <BlogCard blog={blog} />)}
</div>
);
}

In this component we will fetch all the blogs in our database and when the data is returned we can display it one by one with the component called BlogCard.

BlogCard component takes blog data as a prop and displays all the necessary fields.

BlogCard.jsx
export default function BlogCard({ blog }) {
return (
<div
onClick={() => navigation.navigate(`/blog/${blog.slug}`)}
class="shadow-lg w-full rounded-xl overflow-hidden cursor-pointer"
>
<img src={blog.coverImage} alt="" />
<div class="p-7 flex flex-col gap-3">
<span class="text-purple-500 font-[500]">{blog.type}</span>
<h2 class="font-bold">{blog.title}</h2>
<span class="text-sm">{blog.content}</span>
</div>
</div>
);
}

Conclusion

Astro is a new and dynamic web development tool that allows us to use different frameworks under a single roof, and it has gained popularity among developers. Altogic, as a backend as a service platform, can be easily implemented in Astro just like in other frameworks and programming languages. This tutorial has provided us with the necessary knowledge to use Altogic comfortably in Astro.