שילוב KeystoneJS CMS
אנו משלבים את KeystoneJS 6 באתר או באפליקציה שלך, ומגדירים סכמות נתונים, GraphQL API, אימות וממשק ניהול. הצוות שלנו מחבר את KeystoneJS לפרונטאנד של Next.js או React, מטפל בהתקנת PostgreSQL, ומספק CMS headless עובד תוך שישה עד שנים עשר ימים. שילבנו את KeystoneJS עבור צוותי עריכה, קטלוגי מוצרים ואתרי תיעוד. הלקוחות שלנו מקבלים שליטה מלאה על סכמת התוכן שלהם ללא נעילת ספק (vendor lock-in) ועם TypeScript API מהיום הראשון.
KeystoneJS משלב CMS headless ומסגרת אפליקציות. סכמת הנתונים כתובה ב-TypeScript ומייצרת אוטומטית GraphQL API, נקודות קצה דמויות REST וממשק ניהול. עבור צוותים שרוצים שליטה מלאה על מודל התוכן וה-API שלהם ללא ניהול backend נפרד, KeystoneJS מספק בסיס חזק.
מה כלול בשירות שילוב KeystoneJS שלנו
אנו מספקים את שילוב ה-CMS המלא כפרויקט מפתח מלא. ההיקף כולל:
- עיצוב סכמת נתונים עבור סוגי התוכן שלך: פוסטים, עמודים, מוצרים, מחברים וישויות מותאמות אישית
- התקנה והגדרה של KeystoneJS עם PostgreSQL או SQLite
- הגדרת ממשק ניהול עם כללי בקרת גישה לעורכים, מחברים ומנהלים
- GraphQL API עם שאילתות, מוטציות, פילטרים ודפדוף (pagination) מתוייקים
- הגדרת אחסון תמונות וקבצים: דיסק מקומי או אחסון ענן עם S3
- שילוב לקוח Next.js עם GraphQL SDK מתוייק באמצעות graphql-codegen
- Webhooks ו-hooks של שדות מותאמים אישית ללוגיקה עסקית בעת יצירה, עדכון ומחיקה
- כלי עזר ליצירת דפים סטטיים עבור
// keystone.ts import { config, list } from '@keystone-6/core' import { allowAll, denyAll, isSignedIn } from '@keystone-6/core/access' import { text, relationship, password, timestamp, select, checkbox, image, document } from '@keystone-6/core/fields' import { document as documentField } from '@keystone-6/fields-document' export default config({ db: { provider: 'postgresql', url: process.env.DATABASE_URL!, idField: { kind: 'cuid' }, }, lists: { Post: list({ access: { operation: { query: allowAll, create: isSignedIn, update: isSignedIn, delete: isSignedIn, }, }, fields: { title: text({ validation: { isRequired: true } }), slug: text({ isIndexed: 'unique' }), content: documentField({ formatting: true, dividers: true, links: true, layouts: [[1, 1], [1, 2, 1]], }), publishedAt: timestamp(), status: select({ options: ['draft', 'published', 'archived'], defaultValue: 'draft', ui: { displayMode: 'segmented-control' }, }), author: relationship({ ref: 'User.posts' }), tags: relationship({ ref: 'Tag.posts', many: true }), cover: image({ storage: 'local_images' }), }, hooks: { resolveInput: async ({ resolvedData, operation }) => { if (operation === 'create' && !resolvedData.slug) { resolvedData.slug = resolvedData.title ?.toLowerCase() .replace(/\s+/g, '-') .replace(/[^a-z0-9-]/g, '') } return resolvedData }, }, }), Tag: list({ access: allowAll, fields: { name: text({ isIndexed: 'unique' }), posts: relationship({ ref: 'Post.tags', many: true }), }, }), User: list({ access: { operation: { query: isSignedIn, create: ({ session }) => session?.data?.role === 'admin', update: isSignedIn, delete: ({ session }) => session?.data?.role === 'admin', }, }, fields: { name: text({ validation: { isRequired: true } }), email: text({ isIndexed: 'unique', validation: { isRequired: true } }), password: password({ validation: { isRequired: true } }), role: select({ options: ['admin', 'editor', 'author'], defaultValue: 'author' }), posts: relationship({ ref: 'Post.author', many: true }), }, }), }, session: statelessSessions({ secret: process.env.SESSION_SECRET!, maxAge: 60 * 60 * 24 * 30, }), storage: { local_images: { kind: 'local', type: 'image', generateUrl: (path) => `${process.env.ASSET_BASE_URL}/images${path}`, serverRoute: { path: '/images' }, storagePath: 'public/images', }, }, })ו-ISR revalidation
למה KeystoneJS ל-CMS headless?
KeystoneJS מייצר סכמת GraphQL מלאה ממודל הנתונים שלך ב-TypeScript. טיפוסים, מוטציות, פילטרים ומטפלי דפדוף מופיעים אוטומטית. זה מבטל את הצורך בכתיבת ותחזוקת קוד API boilerplate שכל CMS headless דורש.
ניהול סשנים, אימות ובקרת גישה מבוססת תפקידים כלולים מובנים כברירת מחדל. אין צורך לשלב ספריית אימות נפרדת או לנהל middleware מותאם אישית לבקרת גישת עורכים.
הלקוחות שלנו בוחרים ב-KeystoneJS כשהם צריכים מודל תוכן ספציפי לתחום שלהם במקום להתאים את הנתונים לסכמת CMS גנרית. הגישה המבוססת TypeScript גם מבטיחה בטיחות טיפוסים מהמסד נתונים ועד לרכיבי React.
דוגמת סכמת נתונים
// keystone.ts
import { config, list } from '@keystone-6/core'
import { allowAll, denyAll, isSignedIn } from '@keystone-6/core/access'
import { text, relationship, password, timestamp, select, checkbox, image, document } from '@keystone-6/core/fields'
import { document as documentField } from '@keystone-6/fields-document'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL!,
idField: { kind: 'cuid' },
},
lists: {
Post: list({
access: {
operation: {
query: allowAll,
create: isSignedIn,
update: isSignedIn,
delete: isSignedIn,
},
},
fields: {
title: text({ validation: { isRequired: true } }),
slug: text({ isIndexed: 'unique' }),
content: documentField({
formatting: true,
dividers: true,
links: true,
layouts: [[1, 1], [1, 2, 1]],
}),
publishedAt: timestamp(),
status: select({
options: ['draft', 'published', 'archived'],
defaultValue: 'draft',
ui: { displayMode: 'segmented-control' },
}),
author: relationship({ ref: 'User.posts' }),
tags: relationship({ ref: 'Tag.posts', many: true }),
cover: image({ storage: 'local_images' }),
},
hooks: {
resolveInput: async ({ resolvedData, operation }) => {
if (operation === 'create' && !resolvedData.slug) {
resolvedData.slug = resolvedData.title
?.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^a-z0-9-]/g, '')
}
return resolvedData
},
},
}),
Tag: list({
access: allowAll,
fields: {
name: text({ isIndexed: 'unique' }),
posts: relationship({ ref: 'Post.tags', many: true }),
},
}),
User: list({
access: {
operation: {
query: isSignedIn,
create: ({ session }) => session?.data?.role === 'admin',
update: isSignedIn,
delete: ({ session }) => session?.data?.role === 'admin',
},
},
fields: {
name: text({ validation: { isRequired: true } }),
email: text({ isIndexed: 'unique', validation: { isRequired: true } }),
password: password({ validation: { isRequired: true } }),
role: select({
options: ['admin', 'editor', 'author'],
defaultValue: 'author'
}),
posts: relationship({ ref: 'Post.author', many: true }),
},
}),
},
session: statelessSessions({
secret: process.env.SESSION_SECRET!,
maxAge: 60 * 60 * 24 * 30,
}),
storage: {
local_images: {
kind: 'local',
type: 'image',
generateUrl: (path) => `${process.env.ASSET_BASE_URL}/images${path}`,
serverRoute: { path: '/images' },
storagePath: 'public/images',
},
},
}) GraphQL API ושילוב Next.js
לאחר הגדרת הסכמה, KeystoneJS חושף GraphQL API מלא. אנו מגדירים לקוח מתוייק באמצעות graphql-codegen כך שרכיבי הפרונטאנד שלך מקבלים בטיחות טיפוסים בזמן קומפילציה עבור כל השאילתות.
// lib/keystoneClient.ts
import { GraphQLClient } from 'graphql-request'
export const keystoneClient = new GraphQLClient(
process.env.KEYSTONE_API_URL || 'http://localhost:3000/api/graphql',
{
headers: {
'x-api-key': process.env.KEYSTONE_API_KEY!,
},
}
)
// Типизированные запросы через graphql-codegen
import { getSdk } from './__generated__/sdk'
export const cms = getSdk(keystoneClient) // app/blog/[slug]/page.tsx
import { cms } from '@/lib/keystoneClient'
export default async function PostPage({ params }) {
const { post } = await cms.getPostBySlug({ slug: params.slug })
if (!post) notFound()
return <ArticleLayout post={post} />
}
export async function generateStaticParams() {
const { posts } = await cms.getAllPostSlugs()
return posts.map(p => ({ slug: p.slug }))
} שדה מסמך KeystoneJS
שדה המסמך המובנה מאחסן תוכן מובנה במקום HTML גולמי. זה נותן לעורכים חוויית עריכה עשירה ולמפתחים נתונים נקיים לעיבוד.
import { DocumentRenderer } from '@keystone-6/document-renderer'
function PostContent({ content }) {
return (
<DocumentRenderer
document={content.document}
renderers={{
block: {
paragraph: ({ children, textAlign }) => (
<p style={{ textAlign }} className="mb-4">{children}</p>
),
layout: ({ layout, children }) => (
<div className={`grid grid-cols-${layout.join('-')}`}>
{children}
</div>
),
},
inline: {
link: ({ children, href }) => (
<a href={href} className="text-blue-600 underline">{children}</a>
),
},
}}
/>
)
} איך אנו מספקים את השילוב
- סקירת סוגי התוכן שלך והגדרת הסכמה יחד עם הצוות שלך
- הקמת KeystoneJS ומסד נתונים בסביבת פיתוח
- בנייה ובדיקה של הסכמה, ה-API וממשק הניהול
- שילוב לקוח ה-GraphQL בפרונטאנד שלך
- פריסה לסביבת הייצור שלך ומסירת תיעוד
| היקף | ציר זמן |
|---|---|
| 3–4 סוגי תוכן עם אימות בסיסי | 4–6 ימי עבודה |
| 6–8 סוגי תוכן עם אחסון תמונות ו-codegen | 6–8 ימי עבודה |
| שילוב מלא עם hooks מותאמים אישית ואחסון S3 | 8–12 ימי עבודה |
צור קשר כדי לדון בדרישות התוכן שלך. נסקור את מודל הנתונים הקיים שלך, נציע סכמת KeystoneJS ונשלח הצעת מחיר מפורטת תוך יום עסקים אחד.
איך אנו מספקים
אנו מתחילים בשיחת עיצוב סכמה. אנו סוקרים את סוגי התוכן שלך. אנו מציעים את מודל הנתונים. אתה מאשר אותו. אנו מקימים את סביבת הפיתוח. אנו בונים את הסכמה וממשק הניהול. אנו משלבים את לקוח ה-GraphQL בפרונטאנד שלך. אנו פורסים ובודקים. אנו מוסרים קוד ותיעוד. אנו עונים על שאלות במהלך השבוע הראשון שלך בעריכה.







