Yazılar

Notion ile Blog kurmak

Cloudflare üzerinde çalışan ve Notion’dan yazıp düzenleyebildiğiniz bir blog sitesi yapımının anlatımı.

Bu benim uzun zaman sonra yazdığım ilk yazım olacak. Haliyle biraz farklı bir konu üzerine yazmak istedim. Bu yazıda notion ile entegre nasıl ücretsiz [domain alımı hariç] blog sitesi açılır onu anlatacağım. Anlatım olarak detaydan çok konuda takılabilecek anahtar noktalara değinmiş olacağım.

Neden Notion?

Bir süredir kişisel ve iş ile alakalı şeyleri sistematik bir şekilde Notion üzerinde tutuyorum ve bu notları dilediğim gibi özelleştirebilmek çok işime yarıyor. Peki Notion ile entegre blog sitesi açmanın önce avantajı ve dezavantajlarına bakalım;

Avantajları

  1. Kolay kişiselleştirilebilir bir not alma altyapısı
  2. API hizmeti sunması
  3. Mobil ve Desktop üzerinden Notion app ile kolay işlem yapabilme
  4. Bir çok platform ile kolay entegre olabiliyor

Dezavantajları

  1. Notion alışma sürecinde zor olabiliyor, çünkü çok esnek bir alan sunuyor ve ilk başta bu esnekliğin nasıl verimli bir şekilde kurgulanacağını anlamak zaman alabiliyor

Neden Cloudflare?

Cloudflare benim işte kullandığım harika bir platform. Kullandığımız asıl özellikleri DNS yönetimi, Proxy ile SSL hizmeti, Pages ve Workers hizmetleri. Bu hizmetlerin hepsini ücretsiz sunuyor. Pages ile Vue’da yaptığınız bir siteyi çok kolay deploy edebiliyor ve sizi CDN hizmeti ayarlama gibi zahmetlerden kurtarıyor. Ayrıca Worker hizmeti ile günde 100.000 request’i ücretsiz olarak sunuyor.

Bu yazıda bahsi geçen hizmetlerin hepsini kullanmış olacağız. Onun için önce kendinize bir Cloudflare hesabı açmanız gerekiyor.

Kurulum

Notion Database Oluşturma

Önce Notion üzerinde kendinize yeni bir Database açın. Ben kendime Blog adında bir List Database açtım. Property kısmından Publish Date adında bir Date property ekleyin. Bu property ile kendimiz yazılarımızın yayınlanma tarihlerini seçeceğiz.

Notion API Key Oluşturma

Notion API erişimi ile bu Database’e erişim açmanız gerekmekte. Bunun için Notion Integration sayfasına gidip kendinize internal bir integration oluşturun.

Bu entegrasyonu yaptıktan sonra bu API’a oluşturduğunuz Database’e erişim vermeniz gerekmekte. Bunun için Notion app üzerinden database’in sayfasını açtıktan sonra sağ üst köşeden ayarlarına gidip Connections seçeneklerinden oluşturduğunuz API hizmetini seçmelisiniz. Artık API ile Database’e bağlanmaya hazırsınız.

CloudFlare Worker Oluşturma

Notion’ın api desteğindeki zorlayıcı kısım CORS desteğinin olmaması. Bunun için kendinize bir server veya proxy hazırlamanız gerekli. Ben custom server entegrasyonu yerine Cloudflare Worker hizmeti ile bir proxy erişimi denedim. Bu Worker bizim için gerekli CORS header’lar ile Notion API response’ları dönecek, bu sayede tarayıcıdan direk erişebileceğiz. Cloudflare paneli üzerinden yeni bir Worker açıp kod kısmına aşağıdaki kısmı yapıştırın.

const API_END_POINT = "https://api.notion.com/v1";
const SECRET_CODE = "SECRET_HERE";

export default {
  async fetch(request, env) {
    return await handleRequest(request)
  }
}

function getCorsHeaders(request) {
  return {
    "Access-Control-Allow-Origin": request.headers.get("Origin"),
    "Access-Control-Allow-Methods": "GET, HEAD, POST, PUT, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
  };
}

function handleOptions(request) {
  return new Response(null, {
    headers: getCorsHeaders(request),
  });
}

async function handleRequest(request) {
  if (request.method === "OPTIONS") {
    return handleOptions(request);
  }

  let url = new URL(request.url);

  let requestUrl = `${API_END_POINT}${url.pathname}`;

  let notionResponse = await fetch(requestUrl, {
    body: request.body,
    headers: {
      "Authorization": `Bearer ${SECRET_CODE}`,
      "Notion-Version": "2022-06-28",
      "Content-Type": request.headers.get("Content-Type")
    },
    method: request.method
  });

  return new Response(notionResponse.body, {
    headers: {"Content-Type": "application/json", ...getCorsHeaders(request)},
    status: notionResponse.status,
    statusText: notionResponse.statusText
  });
}

Burada SECRET_HERE ile belirtilen alan siz kendi Notion API Key değerinizi koyacaksınız. Bunu yapıp kayıt ettikten sonra Worker’a ayrıca bir custom domain atayabilirsiniz. Bu Worker’ın güzel tarafı sizin API Key değerinizi kimse görmeden işlem yapacak olmanız.

Vue ile Arayüz

API hizmeti ile entegre kendinize şimdi site hazırlayabilirsiniz. Ben burada size Vue ile nasıl entegre edileceğini anlatacağım.

Vue ile kendinize yeni bir proje açtıktan sonra kendinize Cloudflare Worker için bir basit client yazabilirsiniz.

import axios from "axios";

export const client = axios.create({
    baseURL: "CLOUDFLARE_WORKER_URL",
});

export default function callApi(url, method = "get", data, config) {
    return client.request({
        url, method, data, ...config
    })
}

Ben burada callApi adında basit bir metot kullandım.

Yazıları listeleme

Yazıları API ile çekmeden önce Notion’daki database’in id değerini öğrenmeniz gerekli. Bunun için Notion app üzerinden database sayfası açık iken sağ üst köşeden Share tuşuna basıp web’de paylaş kısmında çıkan URL’i kopyalayın ve domain sonrası ve query parameter öncesi değeri seçin. Bu sizin Database id’niz.

export default {
    async setup() {

        const pagesData = (await callApi('/databases/DATABASE_ID/query', "post",
            {
                sorts: [
                    {"property": "Publish Date", "direction": "descending"}
                ]
            }
        )).data;

        return {pagesData};
    }
}

Bu id’yi örnek koddaki gibi kullanarak Database içinde oluşturduğunuz sayfaların listesini alabilirsiniz. Ayrıca sort ile istediğiniz property’e göre sıralama da yapabilirsiniz. Daha detaylı kullanım için Notion API dökümantasyonuna bakabilirsiniz.

Yazı detay sayfası

Notion’ın kendisinin yazıları tutma şekli olarak uyguladığı bir format var. Bu format’a uygun entegrasyon için vue3-notion adında bir kütüphane kullanacağız. Bu kütüphaneye page objesinin id değerini verdiğimizde bizim için içeriği hazırlamış olacak.

<template>
	<NotionRenderer v-if="data" :blockMap="data"/>
</template>

<script>
import {NotionRenderer, getPageBlocks} from "vue3-notion";

export default {
    async setup() {
        const route = useRoute();

        const data = ref(null);

        data.value = await getPageBlocks(route.params.page);

        return {pageData, data};
    }
}
</script>

Vue3-notion’a page id değerini verdikten sonra koddaki gibi page content’i üretebiliyorsunuz.

Cloudflare Pages ile deploy etmek

Cloudflare üzerinden ilk deploy için ilk önce vue projenizi build edip çıktısını panel üzerinden yükleyin. Sonrasında projenizi her update ettiğinizde otomatik şekilde deploy etmesi için github workflow entegrasyonu kurabilirsiniz. Burada tarif edeceğim yöntem docker-compose ile entegre olacak.

Proje içerisinde .github/workflows klasörü açıp publish_blog.yml adında bir dosya oluşturun. Bu dosya ismini kendi isteğinize göre değiştirebilirsiniz.

name: Publish blog
on:
  push:
    branches:
      - main

jobs:
  publish_workflow:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup
        shell: bash
        run: |
          docker-compose -f docker-compose-build.yml run frontend yarn

      - name: Build
        shell: bash
        run: |
          docker-compose -f docker-compose-build.yml run frontend yarn build

      - name: Publish
        uses: cloudflare/wrangler-action@2.0.0
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages publish --project-name=CLOUDFLARE_PAGES_APP_NAME src/dist

CLOUDFLARE_PAGES_APP_NAME ifadesini kendi oluşturduğunuz Cloudflare Pages proje adı ile değiştirin, ardından CLOUDFLARE_ACCOUNT_ID ve CLOUDFLARE_API_TOKEN değerlerini github’da projenin secret kısmına eklemeyi unutmayın. Cloudflare API Key oluşturma sürecinde API Key işlemi olarak Cloudflare Pages hizmetini seçmeniz ve ona read/write izni eklemeniz yeterli olacaktır.

Son olarak Cloudflare Pages üzerinden projenize custom domain eklemeniz ile projeniz artık canlıda olacak.


Ek olarak yapılabilecekler

www.sitename.com → sitename.com yönlendirme

Bu yönlendirme için Cloudflare dökümanında güzel bir yönlendirme eklemiş. Bunu takip ederek kolayca www dahil url’leri apex domain’e(yani ana domain adresine) yönlendirebilirsiniz.

Slug ile url’leri anlaşılır yapma

Bunun için Database’e adı Slug olan bir formula açıp formüle replaceAll(prop("Name"), " ", "-") değerini verip otomatik bir şekilde slug üretebilirsiniz. Bu slug’a sahip page’i çekebilmeniz için page detail sayfasında o filter ile page’i aramanız gerekmekte olacak.

export default {
    async setup() {
        const route = useRoute();

        const pageData = (await callApi('/databases/DATABASE_ID/query', "post",
            {
                "filter": {
                    "and": [{
                        "property": "Slug",
                        "formula": {
                            "string": {
                                "equals": route.params.slug
                            }
                        }
                    }]
                }
            }
        )).data.results[0];

        return {pageData};
    }
}

Bu örnek kodda Slug değeri ile filtrelenip alınan page objesini görebilirsiniz.


Son Olarak

Burada anlattığım tarzda örnek proje olarak bu blog sistemini kurguladım. Yazıyı yazma nedenim de bu platformu yaparken karşılaştıklarımı daha sistematik şekilde not almak ve paylaşmak oldu. Umarım faydalı olmuştur.